diff options
6 files changed, 112 insertions, 78 deletions
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java index 4266d65bfb..2779fcc32d 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java @@ -28,7 +28,6 @@ import org.eclipse.emf.cdo.view.CDOView; import org.eclipse.emf.internal.cdo.bundle.OM; import org.eclipse.emf.internal.cdo.transaction.CDOSavepointImpl; -import org.eclipse.emf.internal.cdo.transaction.CDOTransactionImpl; import org.eclipse.emf.internal.cdo.util.FSMUtil; import org.eclipse.net4j.util.collection.Pair; @@ -213,7 +212,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent private void attachOrReattach(InternalCDOObject object, InternalCDOTransaction transaction) { // Bug 283985 (Re-attachment): Special case: re-attachment - if (((CDOTransactionImpl)transaction).getFormerRevisions().containsKey(object)) + if (transaction.getFormerRevisions().containsKey(object)) { reattachObject(object, transaction); } @@ -631,42 +630,42 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent InternalCDOTransaction transaction = transactionAndContents.getElement1(); List<InternalCDOObject> contents = transactionAndContents.getElement2(); - Map<InternalCDOObject, InternalCDORevision> formerRevisionMap = ((CDOTransactionImpl)transaction) - .getFormerRevisions(); + Map<InternalCDOObject, InternalCDORevision> formerRevisionMap = transaction.getFormerRevisions(); boolean reattaching = formerRevisionMap.containsKey(object); if (!reattaching) { - // Prepare object - CDOID id = transaction.getNextTemporaryID(); - object.cdoInternalSetID(id); - object.cdoInternalSetView(transaction); - changeState(object, CDOState.PREPARED); - - // Create new revision - EClass eClass = object.eClass(); - CDORevisionFactory factory = transaction.getSession().getRevisionManager().getFactory(); - InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass); - revision.setID(id); - revision.setVersion(-1); + // Prepare object + CDOID id = transaction.getNextTemporaryID(); + object.cdoInternalSetID(id); + object.cdoInternalSetView(transaction); + changeState(object, CDOState.PREPARED); + + // Create new revision + EClass eClass = object.eClass(); + CDORevisionFactory factory = transaction.getSession().getRevisionManager().getFactory(); + InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass); + revision.setID(id); + revision.setVersion(-1); - object.cdoInternalSetRevision(revision); + object.cdoInternalSetRevision(revision); - // Register object - transaction.registerObject(object); - transaction.registerNew(object); + // Register object + transaction.registerObject(object); + transaction.registerNew(object); } // Prepare content tree - for (Iterator<InternalCDOObject> it = getProperContents(object,transaction); it.hasNext();) + for (Iterator<InternalCDOObject> it = getProperContents(object, transaction); it.hasNext();) { InternalCDOObject content = it.next(); contents.add(content); INSTANCE.process(content, CDOEvent.PREPARE, transactionAndContents); } } - - private Iterator<InternalCDOObject> getProperContents(final InternalCDOObject object, final CDOTransaction transaction) + + private Iterator<InternalCDOObject> getProperContents(final InternalCDOObject object, + final CDOTransaction transaction) { final boolean isResource = object instanceof Resource; final Iterator<EObject> delegate = object.eContents().iterator(); @@ -746,7 +745,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent public void execute(InternalCDOObject object, CDOState state, CDOEvent event, InternalCDOTransaction transaction) { InternalCDORevisionManager revisionManager = transaction.getSession().getRevisionManager(); - InternalCDORevision formerRevision = ((CDOTransactionImpl)transaction).getFormerRevisions().get(object); + InternalCDORevision formerRevision = transaction.getFormerRevisions().get(object); CDOID id = formerRevision.getID(); object.cdoInternalSetID(id); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java index 3460edf310..e6535985e6 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSavepointImpl.java @@ -66,6 +66,8 @@ public class CDOSavepointImpl extends AbstractSavepoint } }; + private Map<CDOID, CDOObject> reattachedObjects = new HashMap<CDOID, CDOObject>(); + private ConcurrentMap<CDOID, CDORevisionDelta> revisionDeltas = new ConcurrentHashMap<CDOID, CDORevisionDelta>(); /** @@ -80,7 +82,7 @@ public class CDOSavepointImpl extends AbstractSavepoint { super(transaction, lastSavepoint); isDirty = transaction.isDirty(); - if (getPreviousSavepoint() == null) + if (lastSavepoint == null) { sharedDetachedObjects = new HashSet<CDOID>(); } @@ -295,17 +297,34 @@ public class CDOSavepointImpl extends AbstractSavepoint { if (getPreviousSavepoint() == null) { - return Collections.unmodifiableMap(getDetachedObjects()); + Map<CDOID, CDOObject> detachedObjects = getDetachedObjects(); + + // Bug 283985 (Re-attachment): + // Object is only included if it was not reattached in a later savepoint + for (CDOID id : getReattachedObjects().keySet()) + { + detachedObjects.remove(id); + } + + return Collections.unmodifiableMap(detachedObjects); } Map<CDOID, CDOObject> detachedObjects = new HashMap<CDOID, CDOObject>(); + Set<CDOID> reattachedObjectIDs = new HashSet<CDOID>(); // Bug 283985 (Re-attachment) for (CDOSavepointImpl savepoint = this; savepoint != null; savepoint = savepoint.getPreviousSavepoint()) { + reattachedObjectIDs.addAll(savepoint.getReattachedObjects().keySet()); + for (Entry<CDOID, CDOObject> entry : savepoint.getDetachedObjects().entrySet()) { if (!entry.getKey().isTemporary()) { - detachedObjects.put(entry.getKey(), entry.getValue()); + // Bug 283985 (Re-attachment): + // Object is only included if it was not reattached in a later savepoint + if (!reattachedObjectIDs.contains(entry.getKey())) + { + detachedObjects.put(entry.getKey(), entry.getValue()); + } } } } @@ -313,6 +332,12 @@ public class CDOSavepointImpl extends AbstractSavepoint return detachedObjects; } + // Bug 283985 (Re-attachment) + public Map<CDOID, CDOObject> getReattachedObjects() + { + return reattachedObjects; + } + public void recalculateSharedDetachedObjects() { sharedDetachedObjects.clear(); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java index ecdf2c7616..81d5ca2a3b 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java @@ -34,6 +34,7 @@ import org.eclipse.emf.cdo.eresource.EresourceFactory; import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; import org.eclipse.emf.cdo.eresource.impl.CDOResourceNodeImpl; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; import org.eclipse.emf.cdo.transaction.CDOConflictResolver; import org.eclipse.emf.cdo.transaction.CDOSavepoint; @@ -83,6 +84,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; @@ -117,6 +119,9 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa private CDOTransactionStrategy transactionStrategy; + // Bug 283985 (Re-attachment) + private WeakHashMap<InternalCDOObject, InternalCDORevision> formerRevisions = new WeakHashMap<InternalCDOObject, InternalCDORevision>(); + /** * @since 2.0 */ @@ -733,6 +738,10 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa removeObjects(itrSavepoint.getNewResources().values()); removeObjects(itrSavepoint.getNewObjects().values()); + // Bug 283985 (Re-attachment): Objects that were reattached must also be removed + Collection<CDOObject> reattachedObjects = itrSavepoint.getReattachedObjects().values(); + removeObjects(reattachedObjects); + Map<CDOID, CDORevisionDelta> revisionDeltas = itrSavepoint.getRevisionDeltas(); if (!revisionDeltas.isEmpty()) { @@ -769,7 +778,13 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa if (!entryDirtyObject.getKey().isTemporary()) { InternalCDOObject internalDirtyObject = (InternalCDOObject)entryDirtyObject.getValue(); - CDOStateMachine.INSTANCE.rollback(internalDirtyObject); + + // Bug 283985 (Re-attachment): Skip objects that were reattached, because + // they were already reset to TRANSIENT earlier in this method + if (!reattachedObjects.contains(internalDirtyObject)) + { + CDOStateMachine.INSTANCE.rollback(internalDirtyObject); + } } } @@ -907,6 +922,15 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa else { getLastSavepoint().getDetachedObjects().put(object.cdoID(), object); + + if (!formerRevisions.containsKey(object)) + { + formerRevisions.put(object, object.cdoRevision()); + } + + // Object may have been reattached previously, in which case it must + // here be removed from the collection of reattached objects + lastSavepoint.getReattachedObjects().remove(object.cdoID()); } if (!dirty) @@ -1305,6 +1329,34 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa return lastSavepoint.getAllDetachedObjects(); } + public Map<InternalCDOObject, InternalCDORevision> getFormerRevisions() + { + return formerRevisions; + } + + @Override + protected CDOID getID(InternalCDOObject object, boolean onlyPersistedID) + { + // Bug 283985 (Re-attachment): We consult the formerRevision map before delegating + // to the super implementation. Note that we *abuse* the onlyPersistedID + // argument to determine if we should disregard the formerRevision map. We need + // to disregard it when this gets called during commit, and it just so happens + // that only during commit it is being called with onlyPersistedID = false. So + // this code exploits a regularity in the calling code, rather than interpreting + // the onlyPersistedID argument in accordance with its intended meaning. Indeed + // this is a very fragile hack... + if (onlyPersistedID) + { + CDORevision formerRevision = formerRevisions.get(object); + if (formerRevision != null) + { + return formerRevision.getID(); + } + } + + return super.getID(object, onlyPersistedID); + } + /** * @since 2.0 */ diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/FSMUtil.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/FSMUtil.java index a9c7b449fd..103fa4b4af 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/FSMUtil.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/FSMUtil.java @@ -14,7 +14,6 @@ package org.eclipse.emf.internal.cdo.util; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.id.CDOID; -import org.eclipse.emf.cdo.common.model.EMFUtil; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.util.InvalidObjectException; @@ -34,11 +33,9 @@ import org.eclipse.emf.ecore.EGenericType; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.DynamicEObjectImpl; -import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.spi.cdo.InternalCDOObject; import org.eclipse.emf.spi.cdo.InternalCDOView; @@ -253,52 +250,6 @@ public final class FSMUtil } } - /** - * Similar to {@link EcoreUtil#getAllProperContents(Resource, boolean)} except gives only one depth - */ - public static Iterator<InternalCDOObject> getProperContents(final InternalCDOObject object) - { - final boolean isResource = object instanceof Resource; - final CDOView cdoView = object.cdoView(); - final Iterator<EObject> delegate = object.eContents().iterator(); - - return new Iterator<InternalCDOObject>() - { - private Object next; - - public boolean hasNext() - { - while (delegate.hasNext()) - { - InternalEObject eObject = (InternalEObject)delegate.next(); - - EStructuralFeature eContainingFeature = eObject.eContainingFeature(); - if (isResource || eObject.eDirectResource() == null - && (eContainingFeature == null || EMFUtil.isPersistent(eContainingFeature))) - { - next = adapt(eObject, cdoView); - if (next instanceof InternalCDOObject) - { - return true; - } - } - } - - return false; - } - - public InternalCDOObject next() - { - return (InternalCDOObject)next; - } - - public void remove() - { - throw new UnsupportedOperationException(); - } - }; - } - public static Iterator<InternalCDOObject> iterator(final Iterator<?> delegate, final InternalCDOView view) { return new Iterator<InternalCDOObject>() diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java index 07f9f86845..b7293c1b4b 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java @@ -1032,7 +1032,7 @@ public class CDOViewImpl extends Lifecycle implements InternalCDOView // throw new IllegalStateException("Dangling objects not possible outside of a transaction"); // } - private CDOID getID(InternalCDOObject object, boolean onlyPersistedID) + protected CDOID getID(InternalCDOObject object, boolean onlyPersistedID) { if (onlyPersistedID) { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java index a1b38ac82e..9a3af12727 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java @@ -16,6 +16,7 @@ import org.eclipse.emf.cdo.common.id.CDOIDTemp; import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.eresource.CDOResourceFolder; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.transaction.CDOCommitContext; import org.eclipse.emf.cdo.transaction.CDOSavepoint; import org.eclipse.emf.cdo.transaction.CDOTransaction; @@ -23,6 +24,7 @@ import org.eclipse.emf.cdo.transaction.CDOTransaction; import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -63,6 +65,11 @@ public interface InternalCDOTransaction extends CDOTransaction, InternalCDOView public void handleConflicts(Set<CDOObject> conflicts); /** + * @since 3.0 + */ + public Map<InternalCDOObject, InternalCDORevision> getFormerRevisions(); + + /** * Provides a context for a commit operation. * * @author Simon McDuff |