blob: 05aaa2f952cfa1c6e3a4e084a43a12dff6c3646a [file] [log] [blame]
/**
* Copyright (c) 2004 - 2010 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Simon McDuff - bug 201266
* Simon McDuff - bug 213402
*/
package org.eclipse.emf.cdo.spi.server;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.commit.CDOCommitData;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl;
import org.eclipse.emf.cdo.internal.server.bundle.OM;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.IStore;
import org.eclipse.emf.cdo.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.ITransaction;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.om.monitor.OMMonitor;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eike Stepper
* @since 2.0
*/
public abstract class StoreAccessor extends Lifecycle implements IStoreAccessor
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, StoreAccessor.class);
private List<CommitContext> commitContexts = new ArrayList<CommitContext>();
private Store store;
private Object context;
private boolean reader;
private StoreAccessor(Store store, Object context, boolean reader)
{
this.store = store;
this.context = context;
this.reader = reader;
}
protected StoreAccessor(Store store, ISession session)
{
this(store, session, true);
}
protected StoreAccessor(Store store, ITransaction transaction)
{
this(store, transaction, false);
}
public Store getStore()
{
return store;
}
public boolean isReader()
{
return reader;
}
/**
* @since 3.0
*/
public InternalSession getSession()
{
if (context instanceof ITransaction)
{
return (InternalSession)((ITransaction)context).getSession();
}
return (InternalSession)context;
}
public ITransaction getTransaction()
{
if (context instanceof ITransaction)
{
return (ITransaction)context;
}
return null;
}
void setContext(Object context)
{
this.context = context;
}
/**
* @since 3.0
*/
public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint)
{
QueryResourcesContext.ExactMatch context = Store.createExactMatchContext(folderID, name, branchPoint);
queryResources(context);
return context.getResourceID();
}
/**
* @since 3.0
*/
public CDOCommitData loadCommitData(long timeStamp)
{
CommitDataRevisionHandler handler = new CommitDataRevisionHandler(this, timeStamp);
return handler.getCommitData();
}
/**
* @since 3.0
*/
public void write(InternalCommitContext context, OMMonitor monitor)
{
if (TRACER.isEnabled())
{
TRACER.format("Writing transaction: {0}", getTransaction()); //$NON-NLS-1$
}
commitContexts.add(context);
CDOBranch branch = context.getBranchPoint().getBranch();
long timeStamp = context.getBranchPoint().getTimeStamp();
String userID = context.getUserID();
String commitComment = context.getCommitComment();
boolean deltas = store.getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA);
InternalCDOPackageUnit[] newPackageUnits = context.getNewPackageUnits();
InternalCDORevision[] newObjects = context.getNewObjects();
CDOID[] detachedObjects = context.getDetachedObjects();
int dirtyCount = deltas ? context.getDirtyObjectDeltas().length : context.getDirtyObjects().length;
try
{
monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount);
writeCommitInfo(branch, timeStamp, userID, commitComment, monitor.fork());
if (newPackageUnits.length != 0)
{
writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length));
}
addIDMappings(context, monitor.fork());
applyIDMappings(context, monitor);
if (detachedObjects.length != 0)
{
detachObjects(detachedObjects, branch, timeStamp, monitor.fork(detachedObjects.length));
}
if (newObjects.length != 0)
{
writeRevisions(newObjects, branch, monitor.fork(newObjects.length));
}
if (dirtyCount != 0)
{
if (deltas)
{
writeRevisionDeltas(context.getDirtyObjectDeltas(), branch, timeStamp, monitor.fork(dirtyCount));
}
else
{
writeRevisions(context.getDirtyObjects(), branch, monitor.fork(dirtyCount));
}
}
CDODataInput in = context.getLobs();
if (in != null)
{
try
{
int count = in.readInt();
for (int i = 0; i < count; i++)
{
byte[] id = in.readByteArray();
long size = in.readLong();
if (size > 0)
{
writeBlob(id, size, (InputStream)in);
}
else
{
writeClob(id, -size, new InputStreamReader((InputStream)in));
}
}
}
catch (IOException ex)
{
throw WrappedException.wrap(ex);
}
}
}
finally
{
monitor.done();
}
}
/**
* @since 3.0
*/
public final void commit(OMMonitor monitor)
{
doCommit(monitor);
long latest = CDORevision.UNSPECIFIED_DATE;
long latestNonLocal = CDORevision.UNSPECIFIED_DATE;
for (CommitContext commitContext : commitContexts)
{
CDOBranchPoint branchPoint = commitContext.getBranchPoint();
long timeStamp = branchPoint.getTimeStamp();
if (timeStamp > latest)
{
latest = timeStamp;
}
CDOBranch branch = branchPoint.getBranch();
if (!branch.isLocal())
{
if (timeStamp > latestNonLocal)
{
latestNonLocal = timeStamp;
}
}
}
store.setLastCommitTime(latest);
store.setLastNonLocalCommitTime(latestNonLocal);
}
/**
* @since 3.0
*/
protected abstract void doCommit(OMMonitor monitor);
public void rollback()
{
if (TRACER.isEnabled())
{
TRACER.format("Rolling back transaction: {0}", getTransaction()); //$NON-NLS-1$
}
for (CommitContext commitContext : commitContexts)
{
rollback(commitContext);
}
}
protected abstract void rollback(CommitContext commitContext);
public final void release()
{
store.releaseAccessor(this);
commitContexts.clear();
}
/**
* Add ID mappings for all new objects of a transaction to the commit context. The implementor must, for each new
* object of the commit context, determine a permanent CDOID and make it known to the context by calling
* {@link CommitContext#addIDMapping(CDOIDTemp, CDOID)}.
*
* @since 3.0
*/
protected abstract void addIDMappings(InternalCommitContext commitContext, OMMonitor monitor);
/**
* @since 3.0
*/
protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor)
{
context.applyIDMappings(monitor.fork());
}
/**
* @since 3.0
*/
protected abstract void writeCommitInfo(CDOBranch branch, long timeStamp, String userID, String comment,
OMMonitor monitor);
/**
* @since 3.0
*/
protected abstract void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor);
/**
* @since 3.0
*/
protected abstract void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch,
long created, OMMonitor monitor);
/**
* @since 3.0
*/
protected abstract void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor);
/**
* @since 4.0
*/
protected abstract void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException;
/**
* @since 4.0
*/
protected abstract void writeClob(byte[] id, long size, Reader reader) throws IOException;
@Override
protected abstract void doActivate() throws Exception;
@Override
protected abstract void doDeactivate() throws Exception;
protected abstract void doPassivate() throws Exception;
protected abstract void doUnpassivate() throws Exception;
/**
* @author Eike Stepper
* @since 3.0
*/
public static class CommitDataRevisionHandler implements CDORevisionHandler
{
private IStoreAccessor storeAccessor;
private long timeStamp;
private InternalCDORevisionManager revisionManager;
private List<CDOPackageUnit> newPackageUnits = new ArrayList<CDOPackageUnit>();
private List<CDOIDAndVersion> newObjects = new ArrayList<CDOIDAndVersion>();
private List<CDORevisionKey> changedObjects = new ArrayList<CDORevisionKey>();
private List<CDOIDAndVersion> detachedObjects = new ArrayList<CDOIDAndVersion>();
public CommitDataRevisionHandler(IStoreAccessor storeAccessor, long timeStamp)
{
this.storeAccessor = storeAccessor;
this.timeStamp = timeStamp;
InternalStore store = (InternalStore)storeAccessor.getStore();
InternalRepository repository = store.getRepository();
revisionManager = repository.getRevisionManager();
InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
InternalCDOPackageUnit[] packageUnits = packageRegistry.getPackageUnits(timeStamp, timeStamp);
for (InternalCDOPackageUnit packageUnit : packageUnits)
{
if (!packageUnit.isSystem())
{
newPackageUnits.add(packageUnit);
}
}
}
public CDOCommitData getCommitData()
{
storeAccessor.handleRevisions(null, null, timeStamp, this);
return new CDOCommitDataImpl(newPackageUnits, newObjects, changedObjects, detachedObjects);
}
public void handleRevision(CDORevision rev)
{
if (rev.getTimeStamp() != timeStamp)
{
throw new IllegalArgumentException("Invalid revision time stamp: "
+ CDOCommonUtil.formatTimeStamp(rev.getTimeStamp()));
}
if (rev instanceof DetachedCDORevision)
{
detachedObjects.add(rev);
}
else
{
InternalCDORevision revision = (InternalCDORevision)rev;
CDOID id = revision.getID();
CDOBranch branch = revision.getBranch();
int version = revision.getVersion();
if (version > CDOBranchVersion.FIRST_VERSION)
{
CDOBranchVersion oldVersion = branch.getVersion(version - 1);
InternalCDORevision oldRevision = revisionManager.getRevisionByVersion(id, oldVersion, CDORevision.UNCHUNKED,
true);
InternalCDORevisionDelta delta = revision.compare(oldRevision);
changedObjects.add(delta);
}
else
{
InternalCDORevision oldRevision = getRevisionFromBase(id, branch);
if (oldRevision != null)
{
InternalCDORevisionDelta delta = revision.compare(oldRevision);
changedObjects.add(delta);
}
else
{
InternalCDORevision newRevision = revision.copy();
newRevision.setRevised(CDOBranchPoint.UNSPECIFIED_DATE);
newObjects.add(newRevision);
}
}
}
}
private InternalCDORevision getRevisionFromBase(CDOID id, CDOBranch branch)
{
if (branch.isMainBranch())
{
return null;
}
CDOBranchPoint base = branch.getBase();
InternalCDORevision revision = revisionManager.getRevision(id, base, CDORevision.UNCHUNKED,
CDORevision.DEPTH_NONE, true);
if (revision == null)
{
revision = getRevisionFromBase(id, base.getBranch());
}
return revision;
}
}
}