blob: 4603d5848a4b177bf79a27ac334957e55b1513d6 [file] [log] [blame]
/***************************************************************************
* Copyright (c) 2004 - 2008 Eike Stepper, Germany.
* 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:
* Simon McDuff - initial API and implementation
**************************************************************************/
package org.eclipse.emf.cdo.internal.server;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDMetaRange;
import org.eclipse.emf.cdo.common.id.CDOIDObjectFactory;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOPackage;
import org.eclipse.emf.cdo.common.model.core.CDOCorePackage;
import org.eclipse.emf.cdo.common.model.resource.CDOResourcePackage;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionResolver;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.internal.common.revision.CDOIDMapper;
import org.eclipse.emf.cdo.internal.server.bundle.OM;
import org.eclipse.emf.cdo.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.StoreThreadLocal;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackage;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageManager;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.concurrent.RWLockManager;
import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.om.monitor.OMMonitor;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author Simon McDuff
* @since 2.0
*/
public class TransactionCommitContextImpl implements IStoreAccessor.CommitContext, Transaction.InternalCommitContext
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION,
TransactionCommitContextImpl.class);
private TransactionPackageManager packageManager;
private IStoreAccessor accessor;
private long timeStamp = CDORevision.UNSPECIFIED_DATE;
private CDOPackage[] newPackages;
private CDORevision[] newObjects;
private CDORevision[] dirtyObjects;
private CDOID[] detachedObjects;
private List<CDOID> lockedObjects = new ArrayList<CDOID>();
private List<InternalCDORevision> detachedRevisions = new ArrayList<InternalCDORevision>();;
private CDORevisionDelta[] dirtyObjectDeltas;
private List<CDOIDMetaRange> metaIDRanges = new ArrayList<CDOIDMetaRange>();
private ConcurrentMap<CDOIDTemp, CDOID> idMappings = new ConcurrentHashMap<CDOIDTemp, CDOID>();
private CDOIDMapper idMapper = new CDOIDMapper(idMappings);
private String rollbackMessage;
private Transaction transaction;
private boolean autoReleaseLocksEnabled;
public TransactionCommitContextImpl(Transaction transaction)
{
this.transaction = transaction;
packageManager = new TransactionPackageManager();
}
public int getTransactionID()
{
return transaction.getViewID();
}
public Transaction getTransaction()
{
return transaction;
}
public long getTimeStamp()
{
return timeStamp;
}
public TransactionPackageManager getPackageManager()
{
return packageManager;
}
public CDOPackage[] getNewPackages()
{
return newPackages;
}
public CDORevision[] getNewObjects()
{
return newObjects;
}
public CDORevision[] getDirtyObjects()
{
return dirtyObjects;
}
public CDOID[] getDetachedObjects()
{
return detachedObjects;
}
public CDORevisionDelta[] getDirtyObjectDeltas()
{
return dirtyObjectDeltas;
}
public List<CDOIDMetaRange> getMetaIDRanges()
{
return Collections.unmodifiableList(metaIDRanges);
}
public Map<CDOIDTemp, CDOID> getIDMappings()
{
return Collections.unmodifiableMap(idMappings);
}
public void addIDMapping(CDOIDTemp oldID, CDOID newID)
{
if (CDOIDUtil.isNull(newID) || newID.isTemporary())
{
throw new IllegalStateException("newID=" + newID);
}
CDOID previousMapping = idMappings.putIfAbsent(oldID, newID);
if (previousMapping != null)
{
throw new IllegalStateException("previousMapping != null");
}
}
public void applyIDMappings(OMMonitor monitor)
{
try
{
monitor.begin(newObjects.length + dirtyObjects.length + dirtyObjectDeltas.length);
applyIDMappings(newObjects, monitor.fork(newObjects.length));
applyIDMappings(dirtyObjects, monitor.fork(dirtyObjects.length));
for (CDORevisionDelta dirtyObjectDelta : dirtyObjectDeltas)
{
((InternalCDORevisionDelta)dirtyObjectDelta).adjustReferences(idMapper);
monitor.worked();
}
}
finally
{
monitor.done();
}
}
public String getRollbackMessage()
{
return rollbackMessage;
}
public void preCommit()
{
// Allocate a store writer
accessor = transaction.getRepository().getStore().getWriter(transaction);
// Make the store writer available in a ThreadLocal variable
StoreThreadLocal.setAccessor(accessor);
}
public void setNewPackages(CDOPackage[] newPackages)
{
this.newPackages = newPackages;
}
public void setNewObjects(CDORevision[] newObjects)
{
this.newObjects = newObjects;
}
public void setDirtyObjectDeltas(CDORevisionDelta[] dirtyObjectDeltas)
{
this.dirtyObjectDeltas = dirtyObjectDeltas;
}
public void setDetachedObjects(CDOID[] detachedObjects)
{
this.detachedObjects = detachedObjects;
}
public boolean setAutoReleaseLocksEnabled(boolean on)
{
try
{
return autoReleaseLocksEnabled;
}
finally
{
autoReleaseLocksEnabled = on;
}
}
public boolean isAutoReleaseLocksEnabled()
{
return autoReleaseLocksEnabled;
}
/**
* @since 2.0
*/
public void write(OMMonitor monitor)
{
try
{
monitor.begin(106);
// Could throw an exception
timeStamp = createTimeStamp();
dirtyObjects = new CDORevision[dirtyObjectDeltas.length];
adjustMetaRanges(monitor.fork());
adjustTimeStamps(monitor.fork());
Repository repository = (Repository)transaction.getRepository();
computeDirtyObjects(!repository.isSupportingRevisionDeltas(), monitor.fork());
lockObjects();
monitor.worked();
repository.notifyWriteAccessHandlers(transaction, this, monitor.fork());
detachObjects(monitor.fork());
accessor.write(this, monitor.fork(100));
}
catch (Throwable t)
{
handleException(t);
}
finally
{
monitor.done();
}
}
public void commit(OMMonitor monitor)
{
try
{
monitor.begin(101);
accessor.commit(monitor.fork(100));
updateInfraStructure(monitor.fork());
}
catch (RuntimeException ex)
{
handleException(ex);
}
catch (Error ex)
{
handleException(ex);
}
finally
{
monitor.done();
}
}
private void handleException(Throwable ex)
{
OM.LOG.error(ex);
String storeClass = transaction.getRepository().getStore().getClass().getSimpleName();
rollback("Rollback in " + storeClass + ": " + StringUtil.formatException(ex));
}
protected long createTimeStamp()
{
Repository repository = (Repository)transaction.getSession().getSessionManager().getRepository();
return repository.createCommitTimeStamp();
}
public void postCommit(boolean success)
{
try
{
if (success)
{
transaction.getRepository().getNotificationManager().notifyCommit(transaction.getSession(), this);
}
}
finally
{
StoreThreadLocal.release();
accessor = null;
timeStamp = CDORevision.UNSPECIFIED_DATE;
packageManager.clear();
metaIDRanges.clear();
idMappings.clear();
rollbackMessage = null;
newPackages = null;
newObjects = null;
dirtyObjectDeltas = null;
dirtyObjects = null;
}
}
private void adjustTimeStamps(OMMonitor monitor)
{
try
{
monitor.begin(newObjects.length);
for (CDORevision newObject : newObjects)
{
InternalCDORevision revision = (InternalCDORevision)newObject;
revision.setCreated(timeStamp);
monitor.worked();
}
}
finally
{
monitor.done();
}
}
private void adjustMetaRanges(OMMonitor monitor)
{
try
{
monitor.begin(newPackages.length);
for (CDOPackage newPackage : newPackages)
{
if (newPackage.getParentURI() == null)
{
adjustMetaRange(newPackage, monitor.fork());
}
}
}
finally
{
monitor.done();
}
}
private void adjustMetaRange(CDOPackage newPackage, OMMonitor monitor)
{
CDOIDMetaRange oldRange = newPackage.getMetaIDRange();
if (!oldRange.isTemporary())
{
throw new IllegalStateException("!oldRange.isTemporary()");
}
try
{
monitor.begin(oldRange.size());
CDOIDMetaRange newRange = transaction.getRepository().getMetaIDRange(oldRange.size());
((InternalCDOPackage)newPackage).setMetaIDRange(newRange);
for (int l = 0; l < oldRange.size(); l++)
{
CDOIDTemp oldID = (CDOIDTemp)oldRange.get(l);
CDOID newID = newRange.get(l);
if (TRACER.isEnabled())
{
TRACER.format("Mapping meta ID: {0} --> {1}", oldID, newID);
}
idMappings.put(oldID, newID);
monitor.worked();
}
metaIDRanges.add(newRange);
}
finally
{
monitor.done();
}
}
private void lockObjects() throws InterruptedException
{
lockedObjects.clear();
for (int i = 0; i < dirtyObjectDeltas.length; i++)
{
lockedObjects.add(dirtyObjectDeltas[i].getID());
}
for (int i = 0; i < detachedObjects.length; i++)
{
lockedObjects.add(detachedObjects[i]);
}
try
{
LockManager lockManager = ((Repository)transaction.getRepository()).getLockManager();
lockManager.lock(RWLockManager.LockType.WRITE, transaction, lockedObjects, 1000);
}
catch (TimeoutRuntimeException exception)
{
lockedObjects.clear();
throw exception;
}
}
private synchronized void unlockObjects()
{
if (!lockedObjects.isEmpty())
{
LockManager lockManager = ((Repository)transaction.getRepository()).getLockManager();
lockManager.unlock(RWLockManager.LockType.WRITE, transaction, lockedObjects);
lockedObjects.clear();
}
}
private void computeDirtyObjects(boolean failOnNull, OMMonitor monitor)
{
try
{
monitor.begin(dirtyObjectDeltas.length);
for (int i = 0; i < dirtyObjectDeltas.length; i++)
{
dirtyObjects[i] = computeDirtyObject(dirtyObjectDeltas[i], failOnNull);
if (dirtyObjects[i] == null && failOnNull)
{
throw new IllegalStateException("Can not retrieve origin revision for " + dirtyObjectDeltas[i]);
}
monitor.worked();
}
}
finally
{
monitor.done();
}
}
private CDORevision computeDirtyObject(CDORevisionDelta dirtyObjectDelta, boolean loadOnDemand)
{
CDOID id = dirtyObjectDelta.getID();
int version = dirtyObjectDelta.getOriginVersion();
CDORevisionResolver revisionResolver = transaction.getRepository().getRevisionManager();
CDORevision originObject = revisionResolver.getRevisionByVersion(id, CDORevision.UNCHUNKED, version, loadOnDemand);
if (originObject != null)
{
InternalCDORevision dirtyObject = (InternalCDORevision)originObject.copy();
dirtyObjectDelta.apply(dirtyObject);
dirtyObject.setCreated(timeStamp);
// dirtyObject.setVersion(originObject.getVersion() + 1);
return dirtyObject;
}
return null;
}
private void applyIDMappings(CDORevision[] revisions, OMMonitor monitor)
{
try
{
monitor.begin(revisions.length);
for (CDORevision revision : revisions)
{
if (revision != null)
{
InternalCDORevision internalRevision = (InternalCDORevision)revision;
CDOID newID = idMappings.get(internalRevision.getID());
if (newID != null)
{
internalRevision.setID(newID);
}
internalRevision.adjustReferences(idMapper);
monitor.worked();
}
}
}
finally
{
monitor.done();
}
}
public synchronized void rollback(String message)
{
// Check if we already rolledBack
if (rollbackMessage == null)
{
rollbackMessage = message;
unlockObjects();
if (accessor != null)
{
try
{
accessor.rollback();
}
catch (RuntimeException ex)
{
OM.LOG.warn("Problem while rolling back the transaction", ex);
}
}
}
}
protected IStoreAccessor getAccessor()
{
return accessor;
}
private void updateInfraStructure(OMMonitor monitor)
{
try
{
monitor.begin(6);
addNewPackages(monitor.fork());
addRevisions(newObjects, monitor.fork());
addRevisions(dirtyObjects, monitor.fork());
revisedDetachObjects(monitor.fork());
unlockObjects();
monitor.worked();
if (isAutoReleaseLocksEnabled())
{
((Repository)transaction.getRepository()).getLockManager().unlock(transaction);
}
monitor.worked();
}
catch (RuntimeException ex)
{
// TODO Rethink this case
OM.LOG.error("FATAL: Memory infrastructure corrupted after successful commit operation of the store");
}
finally
{
monitor.done();
}
}
private void addNewPackages(OMMonitor monitor)
{
try
{
monitor.begin(newPackages.length);
PackageManager packageManager = (PackageManager)transaction.getRepository().getPackageManager();
for (int i = 0; i < newPackages.length; i++)
{
CDOPackage cdoPackage = newPackages[i];
packageManager.addPackage(cdoPackage);
monitor.worked();
}
}
finally
{
monitor.done();
}
}
private void addRevisions(CDORevision[] revisions, OMMonitor monitor)
{
try
{
monitor.begin(revisions.length);
RevisionManager revisionManager = (RevisionManager)transaction.getRepository().getRevisionManager();
for (CDORevision revision : revisions)
{
if (revision != null)
{
revisionManager.addCachedRevision((InternalCDORevision)revision);
}
monitor.worked();
}
}
finally
{
monitor.done();
}
}
private void revisedDetachObjects(OMMonitor monitor)
{
try
{
monitor.begin(detachedRevisions.size());
for (InternalCDORevision revision : detachedRevisions)
{
revision.setRevised(getTimeStamp() - 1);
monitor.worked();
}
}
finally
{
monitor.done();
}
}
private void detachObjects(OMMonitor monitor)
{
detachedRevisions.clear();
RevisionManager revisionManager = (RevisionManager)transaction.getRepository().getRevisionManager();
CDOID[] detachedObjects = getDetachedObjects();
try
{
monitor.begin(detachedObjects.length);
for (CDOID id : detachedObjects)
{
InternalCDORevision revision = revisionManager.getRevision(id, CDORevision.UNCHUNKED, false);
if (revision != null)
{
detachedRevisions.add(revision);
}
monitor.worked();
}
}
finally
{
monitor.done();
}
}
/**
* @author Eike Stepper
*/
public final class TransactionPackageManager implements InternalCDOPackageManager
{
private List<CDOPackage> newPackages = new ArrayList<CDOPackage>();
private PackageManager repositoryPackageManager = (PackageManager)transaction.getRepository().getPackageManager();
public TransactionPackageManager()
{
}
public void addPackage(CDOPackage cdoPackage)
{
newPackages.add(cdoPackage);
}
public void clear()
{
newPackages.clear();
}
public CDOIDObjectFactory getCDOIDObjectFactory()
{
return repositoryPackageManager.getCDOIDObjectFactory();
}
public CDOPackage lookupPackage(String uri)
{
for (CDOPackage cdoPackage : newPackages)
{
if (ObjectUtil.equals(cdoPackage.getPackageURI(), uri))
{
return cdoPackage;
}
}
return repositoryPackageManager.lookupPackage(uri);
}
public CDOCorePackage getCDOCorePackage()
{
return repositoryPackageManager.getCDOCorePackage();
}
public CDOResourcePackage getCDOResourcePackage()
{
return repositoryPackageManager.getCDOResourcePackage();
}
public void loadPackage(CDOPackage cdoPackage)
{
repositoryPackageManager.loadPackage(cdoPackage);
}
public void loadPackageEcore(CDOPackage cdoPackage)
{
repositoryPackageManager.loadPackageEcore(cdoPackage);
}
public int getPackageCount()
{
throw new UnsupportedOperationException();
}
public CDOPackage[] getPackages()
{
throw new UnsupportedOperationException();
}
public CDOPackage[] getElements()
{
throw new UnsupportedOperationException();
}
public boolean isEmpty()
{
throw new UnsupportedOperationException();
}
public void addListener(IListener listener)
{
throw new UnsupportedOperationException();
}
public void removeListener(IListener listener)
{
throw new UnsupportedOperationException();
}
}
}