Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2019-11-04 04:35:00 -0500
committerEike Stepper2019-11-04 04:35:00 -0500
commitb6750513924f56b0207f5de6094aafa473ddee93 (patch)
treeccda44840f6e970882d3e0707df17d4ca1df2b52
parent44f0bdff5de6c96e4f10a86ebb786c6da2928c1e (diff)
downloadcdo-b6750513924f56b0207f5de6094aafa473ddee93.tar.gz
cdo-b6750513924f56b0207f5de6094aafa473ddee93.tar.xz
cdo-b6750513924f56b0207f5de6094aafa473ddee93.zip
[552633] Support rollback of new objects to their attach-time state
https://bugs.eclipse.org/bugs/show_bug.cgi?id=552633
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java189
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/util/TestAdapter.java41
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransaction.java32
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java13
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java28
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOObjectWrapper.java3
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java512
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java45
8 files changed, 605 insertions, 258 deletions
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java
index a77c3432c4..018fccd598 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java
@@ -10,40 +10,58 @@
*/
package org.eclipse.emf.cdo.tests.bugzilla;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.model1.Category;
+import org.eclipse.emf.cdo.tests.model1.Company;
import org.eclipse.emf.cdo.tests.model4.ContainedElementNoOpposite;
import org.eclipse.emf.cdo.tests.model4.GenRefMultiContained;
import org.eclipse.emf.cdo.tests.model4.RefSingleNonContainedNPL;
-import org.eclipse.emf.cdo.tests.model4.model4Factory;
-import org.eclipse.emf.cdo.tests.model4.model4Package;
import org.eclipse.emf.cdo.tests.util.TestAdapter;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import java.util.HashMap;
+
/**
* @author Laurent Redor
*/
public class Bugzilla_552043_Test extends AbstractCDOTest
{
- // See bug 552043 comment #2 to understand why this test case is disabled.
- public void _testInverseCrossReferencesAfterRollback() throws Exception
+ public void testRollbackInverseCrossReferencer() throws Exception
+ {
+ runRollbackInverseCrossReferencer(false);
+ }
+
+ public void testRollbackInverseCrossReferencer_WithAttachedRevisionsMap() throws Exception
+ {
+ runRollbackInverseCrossReferencer(true);
+ }
+
+ private void runRollbackInverseCrossReferencer(boolean withAttachedRevisionsMap) throws Exception
{
// Ensure that the model is suitable for this test.
- assertTrue(model4Package.eINSTANCE.getGenRefMultiContained_Elements().isMany());
- assertTrue(model4Package.eINSTANCE.getGenRefMultiContained_Elements().isContainment());
- assertTrue(!model4Package.eINSTANCE.getGenRefMultiContained_Elements().isUnsettable());
- assertTrue(!model4Package.eINSTANCE.getRefSingleNonContainedNPL_Element().isMany());
- assertTrue(!model4Package.eINSTANCE.getRefSingleNonContainedNPL_Element().isUnsettable());
+ assertTrue(getModel4Package().getGenRefMultiContained_Elements().isMany());
+ assertTrue(getModel4Package().getGenRefMultiContained_Elements().isContainment());
+ assertTrue(!getModel4Package().getGenRefMultiContained_Elements().isUnsettable());
+ assertTrue(!getModel4Package().getRefSingleNonContainedNPL_Element().isMany());
+ assertTrue(!getModel4Package().getRefSingleNonContainedNPL_Element().isUnsettable());
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
+ if (withAttachedRevisionsMap)
+ {
+ transaction.options().setAttachedRevisionsMap(new HashMap<CDOID, CDORevision>());
+ }
+
CDOResource resource = transaction.createResource(getResourcePath("/res"));
- GenRefMultiContained a = model4Factory.eINSTANCE.createGenRefMultiContained();
- ContainedElementNoOpposite b = model4Factory.eINSTANCE.createContainedElementNoOpposite();
+ GenRefMultiContained a = getModel4Factory().createGenRefMultiContained();
+ ContainedElementNoOpposite b = getModel4Factory().createContainedElementNoOpposite();
resource.getContents().add(a);
resource.getContents().add(b);
transaction.commit();
@@ -52,26 +70,21 @@ public class Bugzilla_552043_Test extends AbstractCDOTest
ECrossReferenceAdapter crossReferencer = new ECrossReferenceAdapter();
resource.eAdapters().add(crossReferencer);
- RefSingleNonContainedNPL c = model4Factory.eINSTANCE.createRefSingleNonContainedNPL();
- a.getElements().add(c);
+ RefSingleNonContainedNPL c = getModel4Factory().createRefSingleNonContainedNPL();
+ a.getElements().add(c); // ATTACH element c.
c.setElement(b);
- // Ensure that cross referencer has recorded the c --> a cross reference.
+ // Ensure that the cross referencer has recorded the c --> a cross reference.
assertEquals("The element c should have one inverse cross reference.", 1, crossReferencer.getInverseReferences(c).size());
assertEquals("The inverse cross reference of element c should be element a.", a, crossReferencer.getInverseReferences(c).iterator().next().getEObject());
- // Ensure that cross referencer has recorded the b --> c cross reference.
+ // Ensure that the cross referencer has recorded the b --> c cross reference.
assertEquals("The element b should have one inverse cross reference.", 1, crossReferencer.getInverseReferences(b).size());
assertEquals("The inverse cross reference of element b should be element c.", c, crossReferencer.getInverseReferences(b).iterator().next().getEObject());
- TestAdapter aAdapter = new TestAdapter();
- a.eAdapters().add(aAdapter);
-
- TestAdapter bAdapter = new TestAdapter();
- b.eAdapters().add(bAdapter);
-
- TestAdapter cAdapter = new TestAdapter();
- c.eAdapters().add(cAdapter);
+ TestAdapter aAdapter = new TestAdapter(a);
+ TestAdapter bAdapter = new TestAdapter(b);
+ TestAdapter cAdapter = new TestAdapter(c);
// Rollback all the changes until the last successful commit.
transaction.rollback();
@@ -85,38 +98,142 @@ public class Bugzilla_552043_Test extends AbstractCDOTest
@SuppressWarnings("unused")
Notification[] cNotifications = cAdapter.getNotifications();
- // The model must be reverted as it was just after the commit.
assertEquals("The element a should contain 0 elements.", 0, a.getElements().size());
- assertEquals("The element c should reference no element.", null, c.getElement());
-
- // Ensure that cross referencer has cleaned the c --> a cross reference.
+ if (withAttachedRevisionsMap)
+ {
+ assertEquals("The element c should reference no element.", null, c.getElement());
+ }
+ else
+ {
+ assertEquals("The element c should reference the b element.", b, c.getElement());
+ }
+
+ // Ensure that the cross referencer has cleaned the c --> a cross reference.
assertEquals("The element c should have 0 inverse cross reference, because it has been removed during rollback.", 0,
crossReferencer.getInverseReferences(c).size());
- // Ensure that cross referencer has cleaned the b --> c cross reference.
- assertEquals("The element b should have 0 inverse cross reference, because it has been unset from element c during rollback.", 0,
- crossReferencer.getInverseReferences(b).size());
+ if (withAttachedRevisionsMap)
+ {
+ // Ensure that the cross referencer has cleaned the b --> c cross reference.
+ assertEquals("The element b should have 0 inverse cross reference, because it has been unset from element c during rollback.", 0,
+ crossReferencer.getInverseReferences(b).size());
+ }
+ else
+ {
+ // Ensure that the cross referencer has NOT cleaned the b --> c cross reference.
+ assertEquals("The element b should have 1 inverse cross reference, because it has NOT been unset from element c during rollback.", 1,
+ crossReferencer.getInverseReferences(b).size());
+ assertEquals("The inverse cross reference of element b should be element c.", c, crossReferencer.getInverseReferences(b).iterator().next().getEObject());
+ }
}
public void testRollbackNewObject() throws Exception
{
+ runRollbackNewObject(false);
+ }
+
+ public void testRollbackNewObject_WithAttachedRevisionsMap() throws Exception
+ {
+ runRollbackNewObject(true);
+ }
+
+ private void runRollbackNewObject(boolean withAttachedRevisionsMap) throws Exception
+ {
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
+ if (withAttachedRevisionsMap)
+ {
+ transaction.options().setAttachedRevisionsMap(new HashMap<CDOID, CDORevision>());
+ }
+
CDOResource resource = transaction.createResource(getResourcePath("/res"));
- GenRefMultiContained a = model4Factory.eINSTANCE.createGenRefMultiContained();
- ContainedElementNoOpposite b = model4Factory.eINSTANCE.createContainedElementNoOpposite();
+ GenRefMultiContained a = getModel4Factory().createGenRefMultiContained();
+ ContainedElementNoOpposite b = getModel4Factory().createContainedElementNoOpposite();
resource.getContents().add(a);
resource.getContents().add(b);
transaction.commit();
- RefSingleNonContainedNPL c = model4Factory.eINSTANCE.createRefSingleNonContainedNPL();
- a.getElements().add(c);
+ RefSingleNonContainedNPL c = getModel4Factory().createRefSingleNonContainedNPL();
+ a.getElements().add(c); // ATTACH element c.
c.setElement(b);
// Rollback all the changes until the last successful commit.
transaction.rollback();
- // Element c must NOT be reverted as it was just after the commit.
- assertEquals("The element c should still reference element b.", b, c.getElement());
+ assertEquals("The element c should still reference element b.", withAttachedRevisionsMap ? null : b, c.getElement());
+ }
+
+ public void testRollbackMultipleNewObject() throws Exception
+ {
+ runRollbackMultipleNewObject(false);
+ }
+
+ public void testRollbackMultipleNewObject_WithAttachedRevisionsMap() throws Exception
+ {
+ runRollbackMultipleNewObject(true);
+ }
+
+ private void runRollbackMultipleNewObject(boolean withAttachedRevisionsMap) throws Exception
+ {
+ Category category0 = getModel1Factory().createCategory();
+ category0.setName("category0");
+
+ Category category1 = getModel1Factory().createCategory();
+ category1.setName("category1");
+ category0.getCategories().add(category1);
+
+ Category category2a = getModel1Factory().createCategory();
+ category2a.setName("category2a");
+ category1.getCategories().add(category2a);
+
+ Category category2b = getModel1Factory().createCategory();
+ category2b.setName("category2b");
+ category1.getCategories().add(category2b);
+
+ Category category2c = getModel1Factory().createCategory();
+ category2c.setName("category2c");
+ category1.getCategories().add(category2c);
+
+ CDOSession session = openSession();
+ CDOTransaction transaction = session.openTransaction();
+ if (withAttachedRevisionsMap)
+ {
+ transaction.options().setAttachedRevisionsMap(new HashMap<CDOID, CDORevision>());
+ }
+
+ CDOResource resource = transaction.createResource(getResourcePath("/res"));
+ Company company = getModel1Factory().createCompany();
+ resource.getContents().add(company);
+ transaction.commit();
+
+ company.getCategories().add(category1);
+ category2a.setName("xxx2a");
+ category2b.setName("xxx2b");
+ category2c.setName("xxx2c");
+ assertEquals(company, category1.eContainer());
+ assertEquals(category1, category2a.eContainer());
+ assertEquals(category1, category2b.eContainer());
+ assertEquals(category1, category2c.eContainer());
+ assertEquals("xxx2a", category2a.getName());
+ assertEquals("xxx2b", category2b.getName());
+ assertEquals("xxx2c", category2c.getName());
+
+ TestAdapter adapter1 = new TestAdapter(category1);
+ TestAdapter adapter2a = new TestAdapter(category2a);
+ TestAdapter adapter2b = new TestAdapter(category2b);
+ TestAdapter adapter2c = new TestAdapter(category2c);
+
+ transaction.rollback();
+ assertEquals(null, category1.eContainer());
+ assertEquals(category1, category2a.eContainer());
+ assertEquals(category1, category2b.eContainer());
+ assertEquals(category1, category2c.eContainer());
+ assertEquals(withAttachedRevisionsMap ? "category2a" : "xxx2a", category2a.getName());
+ assertEquals(withAttachedRevisionsMap ? "category2b" : "xxx2b", category2b.getName());
+ assertEquals(withAttachedRevisionsMap ? "category2c" : "xxx2c", category2c.getName());
+ adapter1.assertNotifications(1);
+ adapter2a.assertNotifications(withAttachedRevisionsMap ? 1 : 0);
+ adapter2b.assertNotifications(withAttachedRevisionsMap ? 1 : 0);
+ adapter2c.assertNotifications(withAttachedRevisionsMap ? 1 : 0);
}
}
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/util/TestAdapter.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/util/TestAdapter.java
index 3b3b924c57..b17d428d01 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/util/TestAdapter.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/util/TestAdapter.java
@@ -10,6 +10,8 @@
*/
package org.eclipse.emf.cdo.tests.util;
+import static org.junit.Assert.assertEquals;
+
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
@@ -22,7 +24,7 @@ import java.util.List;
*/
public class TestAdapter implements Adapter
{
- private List<Notification> notifications = new ArrayList<Notification>();
+ private final List<Notification> notifications = new ArrayList<Notification>();
private Notifier notifier;
@@ -30,11 +32,34 @@ public class TestAdapter implements Adapter
{
}
+ public TestAdapter(Notifier notifier)
+ {
+ notifier.eAdapters().add(this);
+ }
+
public Notifier getTarget()
{
return notifier;
}
+ public void setTarget(Notifier newTarget)
+ {
+ notifier = newTarget;
+ }
+
+ public boolean isAdapterForType(Object type)
+ {
+ return false;
+ }
+
+ public void notifyChanged(Notification notification)
+ {
+ synchronized (notifications)
+ {
+ notifications.add(notification);
+ }
+ }
+
public Notification[] getNotifications()
{
synchronized (notifications)
@@ -51,21 +76,11 @@ public class TestAdapter implements Adapter
}
}
- public boolean isAdapterForType(Object type)
- {
- return false;
- }
-
- public void notifyChanged(Notification notification)
+ public void assertNotifications(int expectedSize)
{
synchronized (notifications)
{
- notifications.add(notification);
+ assertEquals(expectedSize, notifications.size());
}
}
-
- public void setTarget(Notifier newTarget)
- {
- notifier = newTarget;
- }
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransaction.java
index 1590a179b1..f5cc67fe4d 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransaction.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOTransaction.java
@@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
import org.eclipse.emf.cdo.common.commit.CDOChangeSetDataProvider;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.util.CDOResourceNodeNotFoundException;
import org.eclipse.emf.cdo.eresource.CDOBinaryResource;
@@ -448,6 +449,24 @@ public interface CDOTransaction extends CDOView, CDOCommonTransaction, CDOUserTr
public void removeAutoReleaseLocksExemptions(boolean recursive, EObject... objects);
/**
+ * Returns a map which, if non-<code>null</code>, stores copies of the initial {@link CDORevision revisions} of newly attached objects.
+ *
+ * @see #setAttachedRevisionsMap(Map)
+ * @since 4.8
+ */
+ public Map<CDOID, CDORevision> getAttachedRevisionsMap();
+
+ /**
+ * Sets a map which, if non-<code>null</code>, stores copies of the initial {@link CDORevision revisions} of newly attached objects,
+ * so that these objects can and will be rolled back to the model values they had at attachment time. If this map is <code>null</code>
+ * newly attached objects will keep the model values they have at rollback time. Note that remembering copies of all newly attached objects
+ * can impose resource problems when many objects are attached, e.g., during larger imports.
+ *
+ * @since 4.8
+ */
+ public void setAttachedRevisionsMap(Map<CDOID, CDORevision> attachedRevisionsMap);
+
+ /**
* Returns the number of milliseconds to wait for the transaction update when {@link CDOTransaction#commit()} is called.
* <p>
* Default value is 10000.
@@ -544,6 +563,19 @@ public interface CDOTransaction extends CDOView, CDOCommonTransaction, CDOUserTr
/**
* An {@link IOptionsEvent options event} fired from transaction {@link CDOTransaction#options() options} when the
+ * {@link Options#setAttachedRevisionsMap(Map) attached revisions map} option has changed.
+ *
+ * @author Eike Stepper
+ * @since 4.8
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+ public interface AttachedRevisionsMap extends IOptionsEvent
+ {
+ }
+
+ /**
+ * An {@link IOptionsEvent options event} fired from transaction {@link CDOTransaction#options() options} when the
* {@link Options#setCommitInfoTimeout(long) commit info timeout} option has changed.
*
* @author Eike Stepper
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
index 3bdbadab06..43fe9b94d7 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java
@@ -519,11 +519,16 @@ public class CDOObjectImpl extends MinimalEStoreEObjectImpl implements InternalC
}
InternalCDOClassInfo classInfo = cdoClassInfo();
-
CDOStore store = cdoStore();
- super.eSetDirectResource((Resource.Internal)store.getResource(this));
- eBasicSetContainer(store.getContainer(this));
- eBasicSetContainerFeatureID(store.getContainingFeatureID(this));
+
+ Resource.Internal resource = (Resource.Internal)store.getResource(this);
+ super.eSetDirectResource(resource);
+
+ InternalEObject container = store.getContainer(this);
+ eBasicSetContainer(container);
+
+ int containingFeatureID = store.getContainingFeatureID(this);
+ eBasicSetContainerFeatureID(containingFeatureID);
if (eSettings != null)
{
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
index d57789370e..f3b22831e2 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
@@ -394,6 +394,34 @@ public abstract class CDOLegacyWrapper extends CDOObjectWrapper
}
}
+ @Override
+ public void cdoInternalRollback(InternalCDORevision revision)
+ {
+ cdoInternalSetRevision(revision);
+ boolean bypassPermissionChecks = revision.bypassPermissionChecks(true);
+ boolean deliver = instance.eDeliver();
+ if (deliver)
+ {
+ instance.eSetDeliver(false);
+ }
+
+ try
+ {
+ for (EStructuralFeature feature : classInfo.getAllPersistentFeatures())
+ {
+ revisionToInstanceFeature(feature);
+ }
+ }
+ finally
+ {
+ revision.bypassPermissionChecks(bypassPermissionChecks);
+ if (deliver)
+ {
+ instance.eSetDeliver(true);
+ }
+ }
+ }
+
protected void instanceToRevision()
{
InternalCDORevision revision = cdoRevision();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOObjectWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOObjectWrapper.java
index f13c2572e6..27c92d23bf 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOObjectWrapper.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOObjectWrapper.java
@@ -12,6 +12,7 @@ package org.eclipse.emf.internal.cdo.object;
import org.eclipse.emf.cdo.CDOLock;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.internal.cdo.CDOObjectImpl;
@@ -57,4 +58,6 @@ public abstract class CDOObjectWrapper extends CDOObjectWrapperBase implements I
{
return CDOObjectImpl.getLockState(this);
}
+
+ public abstract void cdoInternalRollback(InternalCDORevision revision);
}
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 46a5aae5de..e8017cc1e3 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
@@ -1828,7 +1828,198 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
}
}
- private Set<CDOID> rollbackCompletely(CDOUserSavepoint savepoint)
+ /**
+ * @since 2.0
+ */
+ public void detachObject(InternalCDOObject object)
+ {
+ synchronized (getViewMonitor())
+ {
+ lockView();
+
+ try
+ {
+ CDOTransactionHandler1[] handlers = getTransactionHandlers1();
+ for (int i = 0; i < handlers.length; i++)
+ {
+ CDOTransactionHandler1 handler = handlers[i];
+ handler.detachingObject(this, object);
+ }
+
+ // deregister object
+ CDOID id = object.cdoID();
+ if (object.cdoState() == CDOState.NEW)
+ {
+ Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
+
+ // Determine if we added object
+ if (map.containsKey(id))
+ {
+ map.remove(id);
+ }
+ else
+ {
+ lastSavepoint.getDetachedObjects().put(id, object);
+ }
+
+ // deregister object
+ deregisterObject(object);
+ }
+ else
+ {
+ if (!cleanRevisions.containsKey(object))
+ {
+ cleanRevisions.put(object, object.cdoRevision());
+ }
+
+ lastSavepoint.getDetachedObjects().put(id, object);
+
+ // Object may have been reattached previously, in which case it must
+ // here be removed from the collection of reattached objects
+ lastSavepoint.getReattachedObjects().remove(id);
+ }
+
+ getUnitManager().removeObject(object);
+ }
+ finally
+ {
+ unlockView();
+ }
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void handleRollback(InternalCDOSavepoint savepoint)
+ {
+ if (savepoint == null)
+ {
+ throw new IllegalArgumentException(Messages.getString("CDOTransactionImpl.3")); //$NON-NLS-1$
+ }
+
+ if (savepoint.getTransaction() != this)
+ {
+ throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.4"), savepoint)); //$NON-NLS-1$
+ }
+
+ if (!savepoint.isValid())
+ {
+ throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.6"), savepoint)); //$NON-NLS-1$
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("handleRollback()"); //$NON-NLS-1$
+ }
+
+ synchronized (getViewMonitor())
+ {
+ lockView();
+
+ try
+ {
+ // Remember current revisions
+ Map<CDOObject, CDORevision> oldRevisions = new HashMap<CDOObject, CDORevision>();
+ collectRevisions(oldRevisions, getDirtyObjects());
+ collectRevisions(oldRevisions, getNewObjects());
+
+ // Rollback objects
+ Map<CDOObject, CDORevision> newRevisions = new HashMap<CDOObject, CDORevision>();
+ Set<CDOID> idsOfNewObjectWithDeltas = rollbackCompletely(savepoint, newRevisions);
+
+ lastSavepoint = savepoint;
+ lastSavepoint.setNextSavepoint(null);
+ lastSavepoint.clear();
+
+ // Load from first savepoint up to current savepoint
+ loadSavepoint(lastSavepoint, idsOfNewObjectWithDeltas);
+
+ if (lastSavepoint == firstSavepoint && options().isAutoReleaseLocksEnabled())
+ {
+ CDORepositoryInfo repositoryInfo = getSession().getRepositoryInfo();
+ if (isDurableView() && repositoryInfo.getState() == CDOCommonRepository.State.ONLINE || repositoryInfo.getType() == CDOCommonRepository.Type.MASTER)
+ {
+ // Unlock all objects
+ unlockObjects(null, null);
+ }
+ }
+
+ // Send notifications.
+ for (Entry<CDOObject, CDORevision> entry : oldRevisions.entrySet())
+ {
+ InternalCDOObject object = (InternalCDOObject)entry.getKey();
+ InternalCDORevision oldRevision = (InternalCDORevision)entry.getValue();
+
+ InternalCDORevision newRevision = object.cdoRevision();
+ if (newRevision == null)
+ {
+ newRevision = (InternalCDORevision)newRevisions.get(object);
+ }
+
+ if (newRevision == null && !FSMUtil.isTransient(object))
+ {
+ // This can happen for example for conflicting objects which have been set to PROXY in rollbackCompletely().
+ CDOID id = oldRevision.getID();
+ newRevision = getRevision(id, true);
+ object.cdoInternalSetRevision(newRevision);
+ object.cdoInternalSetState(CDOState.CLEAN);
+ }
+
+ if (newRevision != null)
+ {
+ InternalCDORevisionDelta delta = newRevision.compare(oldRevision);
+ if (!delta.isEmpty())
+ {
+ Set<CDOObject> detachedObjects = Collections.emptySet();
+
+ CDONotificationBuilder builder = new CDONotificationBuilder(this);
+ NotificationChain notification = builder.buildNotification(object, oldRevision, delta, detachedObjects);
+ if (notification != null)
+ {
+ notification.dispatch();
+ }
+ }
+ }
+ }
+
+ IListener[] listeners = getListeners();
+ if (listeners != null)
+ {
+ fireEvent(new FinishedEvent(true), listeners);
+ }
+
+ CDOTransactionHandler2[] handlers = getTransactionHandlers2();
+ for (int i = 0; i < handlers.length; i++)
+ {
+ CDOTransactionHandler2 handler = handlers[i];
+
+ try
+ {
+ handler.rolledBackTransaction(this);
+ }
+ catch (RuntimeException ex)
+ {
+ OM.LOG.error(ex);
+ }
+ }
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ throw new TransactionException(ex);
+ }
+ finally
+ {
+ unlockView();
+ }
+ }
+ }
+
+ private Set<CDOID> rollbackCompletely(CDOUserSavepoint savepoint, Map<CDOObject, CDORevision> newRevisions)
{
Set<CDOID> idsOfNewObjectsWithDeltas = new HashSet<CDOID>();
Set<InternalCDOObject> newObjects = new HashSet<InternalCDOObject>();
@@ -1872,16 +2063,41 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
}
}
+ Map<CDOID, CDORevision> attachedRevisions = options().getAttachedRevisionsMap();
for (Object idOrObject : toBeDetached)
{
- if (idOrObject instanceof CDOObjectImpl)
+ InternalCDORevision revision = null;
+
+ if (idOrObject instanceof InternalCDOObject)
{
- CDOObjectImpl impl = (CDOObjectImpl)idOrObject;
- InternalCDORevision revision = impl.cdoRevision();
+ InternalCDOObject object = (InternalCDOObject)idOrObject;
+ CDOID id = object.cdoID();
+
+ if (attachedRevisions != null)
+ {
+ revision = (InternalCDORevision)attachedRevisions.get(id);
+ }
+
+ if (revision == null)
+ {
+ revision = object.cdoRevision();
+ if (revision != null)
+ {
+ // Copy the revision, so that the revision modifications below don't apply to the oldRevisions,
+ // which have been remembered in handleRollback() for the computation of delta notifications.
+ revision = revision.copy();
+ }
+ }
- Resource.Internal directResource = impl.eDirectResource();
- EObject container = impl.eContainer();
- if (!toBeDetached.contains(directResource) && !toBeDetached.contains(container))
+ if (revision != null)
+ {
+ newRevisions.put((CDOObject)idOrObject, revision);
+ }
+
+ Resource.Internal directResource = object.eDirectResource();
+ EObject container = object.eContainer();
+ boolean topLevel = !toBeDetached.contains(directResource) && !toBeDetached.contains(container);
+ if (topLevel)
{
if (revision != null)
{
@@ -1892,22 +2108,36 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
revision.setContainerID(null);
revision.setContainingFeatureID(0);
}
- else
+ }
+
+ if (idOrObject instanceof CDOObjectImpl)
+ {
+ CDOObjectImpl impl = (CDOObjectImpl)idOrObject;
+
+ if (revision != null)
+ {
+ impl.cdoInternalSetRevision(revision);
+ }
+ else if (topLevel)
{
// Unset direct resource and eContainer in the EObject.
impl.cdoInternalSetResource(null);
}
}
- }
- else if (idOrObject instanceof CDOObjectWrapper)
- {
- CDOObjectWrapper wrapper = (CDOObjectWrapper)idOrObject;
- Resource.Internal directResource = wrapper.eDirectResource();
- EObject container = wrapper.eContainer();
- if (!toBeDetached.contains(directResource) && !toBeDetached.contains(container))
+ else if (idOrObject instanceof CDOObjectWrapper)
{
- wrapper.setInstanceResource(null);
- wrapper.setInstanceContainer(null, 0);
+ CDOObjectWrapper wrapper = (CDOObjectWrapper)idOrObject;
+
+ if (revision != null)
+ {
+ wrapper.cdoInternalRollback(revision);
+ }
+
+ if (topLevel)
+ {
+ wrapper.setInstanceResource(null);
+ wrapper.setInstanceContainer(null, 0);
+ }
}
}
}
@@ -1925,7 +2155,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
}
}
- // Rollback all detached objects
+ // Rollback all detached objects.
Map<CDOID, CDOObject> detachedObjectsMap = itrSavepoint.getDetachedObjects();
if (!detachedObjectsMap.isEmpty())
{
@@ -1945,6 +2175,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
}
}
+ // Rollback dirty objects.
for (Entry<CDOID, CDOObject> entryDirtyObject : itrSavepoint.getDirtyObjects().entrySet())
{
CDOID id = entryDirtyObject.getKey();
@@ -1967,6 +2198,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
}
}
+ // Rollback new objects.
for (InternalCDOObject internalCDOObject : newObjects)
{
if (FSMUtil.isNew(internalCDOObject))
@@ -2060,195 +2292,14 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
dirty = savepoint.wasDirty();
}
- /**
- * @since 2.0
- */
- public void detachObject(InternalCDOObject object)
- {
- synchronized (getViewMonitor())
- {
- lockView();
-
- try
- {
- CDOTransactionHandler1[] handlers = getTransactionHandlers1();
- for (int i = 0; i < handlers.length; i++)
- {
- CDOTransactionHandler1 handler = handlers[i];
- handler.detachingObject(this, object);
- }
-
- // deregister object
- CDOID id = object.cdoID();
- if (object.cdoState() == CDOState.NEW)
- {
- Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
-
- // Determine if we added object
- if (map.containsKey(id))
- {
- map.remove(id);
- }
- else
- {
- lastSavepoint.getDetachedObjects().put(id, object);
- }
-
- // deregister object
- deregisterObject(object);
- }
- else
- {
- if (!cleanRevisions.containsKey(object))
- {
- cleanRevisions.put(object, object.cdoRevision());
- }
-
- lastSavepoint.getDetachedObjects().put(id, object);
-
- // Object may have been reattached previously, in which case it must
- // here be removed from the collection of reattached objects
- lastSavepoint.getReattachedObjects().remove(id);
- }
-
- getUnitManager().removeObject(object);
- }
- finally
- {
- unlockView();
- }
- }
- }
-
- /**
- * @since 2.0
- */
- public void handleRollback(InternalCDOSavepoint savepoint)
+ private void collectRevisions(Map<CDOObject, CDORevision> revisions, Map<CDOID, CDOObject> objects)
{
- if (savepoint == null)
- {
- throw new IllegalArgumentException(Messages.getString("CDOTransactionImpl.3")); //$NON-NLS-1$
- }
-
- if (savepoint.getTransaction() != this)
- {
- throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.4"), savepoint)); //$NON-NLS-1$
- }
-
- if (!savepoint.isValid())
- {
- throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.6"), savepoint)); //$NON-NLS-1$
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.trace("handleRollback()"); //$NON-NLS-1$
- }
-
- synchronized (getViewMonitor())
+ for (CDOObject object : objects.values())
{
- lockView();
-
- try
- {
- // Remember current revisions
- Map<CDOObject, CDORevision> oldRevisions = new HashMap<CDOObject, CDORevision>();
- for (CDOObject object : getDirtyObjects().values())
- {
- CDORevision oldRevision = object.cdoRevision();
- if (oldRevision != null)
- {
- oldRevisions.put(object, oldRevision);
- }
- }
-
- // Rollback objects
- Set<CDOID> idsOfNewObjectWithDeltas = rollbackCompletely(savepoint);
-
- lastSavepoint = savepoint;
- lastSavepoint.setNextSavepoint(null);
- lastSavepoint.clear();
-
- // Load from first savepoint up to current savepoint
- loadSavepoint(lastSavepoint, idsOfNewObjectWithDeltas);
-
- if (lastSavepoint == firstSavepoint && options().isAutoReleaseLocksEnabled())
- {
- CDORepositoryInfo repositoryInfo = getSession().getRepositoryInfo();
- if (isDurableView() && repositoryInfo.getState() == CDOCommonRepository.State.ONLINE || repositoryInfo.getType() == CDOCommonRepository.Type.MASTER)
- {
- // Unlock all objects
- unlockObjects(null, null);
- }
- }
-
- // Send notifications
- for (Entry<CDOObject, CDORevision> entry : oldRevisions.entrySet())
- {
- InternalCDOObject object = (InternalCDOObject)entry.getKey();
- if (FSMUtil.isTransient(object))
- {
- continue;
- }
-
- InternalCDORevision oldRevision = (InternalCDORevision)entry.getValue();
- InternalCDORevision newRevision = object.cdoRevision();
- if (newRevision == null)
- {
- newRevision = getRevision(oldRevision.getID(), true);
- object.cdoInternalSetRevision(newRevision);
- object.cdoInternalSetState(CDOState.CLEAN);
- }
-
- if (newRevision != null)
- {
- InternalCDORevisionDelta delta = newRevision.compare(oldRevision);
- if (!delta.isEmpty())
- {
- Set<CDOObject> detachedObjects = Collections.emptySet();
-
- CDONotificationBuilder builder = new CDONotificationBuilder(this);
- NotificationChain notification = builder.buildNotification(object, oldRevision, delta, detachedObjects);
- if (notification != null)
- {
- notification.dispatch();
- }
- }
- }
- }
-
- IListener[] listeners = getListeners();
- if (listeners != null)
- {
- fireEvent(new FinishedEvent(true), listeners);
- }
-
- CDOTransactionHandler2[] handlers = getTransactionHandlers2();
- for (int i = 0; i < handlers.length; i++)
- {
- CDOTransactionHandler2 handler = handlers[i];
-
- try
- {
- handler.rolledBackTransaction(this);
- }
- catch (RuntimeException ex)
- {
- OM.LOG.error(ex);
- }
- }
- }
- catch (RuntimeException ex)
- {
- throw ex;
- }
- catch (Exception ex)
+ CDORevision oldRevision = object.cdoRevision();
+ if (oldRevision != null)
{
- throw new TransactionException(ex);
- }
- finally
- {
- unlockView();
+ revisions.put(object, oldRevision);
}
}
}
@@ -2626,6 +2677,13 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
firstSavepoint.setNextSavepoint(null);
cleanRevisions.clear();
+
+ Map<CDOID, CDORevision> attachedRevisions = options().getAttachedRevisionsMap();
+ if (attachedRevisions != null)
+ {
+ attachedRevisions.clear();
+ }
+
dirty = false;
conflict = 0;
idGenerator.reset();
@@ -4910,6 +4968,8 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
private long commitInfoTimeout = DEFAULT_COMMIT_INFO_TIMEOUT;
+ private Map<CDOID, CDORevision> attachedRevisionsMap;
+
public OptionsImpl()
{
}
@@ -5304,6 +5364,37 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
return effectiveAutoReleaseLock;
}
+ public Map<CDOID, CDORevision> getAttachedRevisionsMap()
+ {
+ return attachedRevisionsMap;
+ }
+
+ public void setAttachedRevisionsMap(Map<CDOID, CDORevision> attachedRevisionsMap)
+ {
+ checkActive();
+
+ IEvent event = null;
+ synchronized (getViewMonitor())
+ {
+ lockView();
+
+ try
+ {
+ if (this.attachedRevisionsMap != attachedRevisionsMap)
+ {
+ this.attachedRevisionsMap = attachedRevisionsMap;
+ event = new AttachedRevisionsMapImpl();
+ }
+ }
+ finally
+ {
+ unlockView();
+ }
+ }
+
+ fireEvent(event);
+ }
+
public long getCommitInfoTimeout()
{
return commitInfoTimeout;
@@ -5403,6 +5494,19 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
/**
* @author Eike Stepper
*/
+ private final class AttachedRevisionsMapImpl extends OptionsEvent implements AttachedRevisionsMap
+ {
+ private static final long serialVersionUID = 1L;
+
+ public AttachedRevisionsMapImpl()
+ {
+ super(OptionsImpl.this);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
private final class CommitInfoTimeoutImpl extends OptionsEvent implements CommitInfoTimeout
{
private static final long serialVersionUID = 1L;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
index df9815fe29..795f0a4c7d 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
@@ -942,7 +942,20 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
{
public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object NULL)
{
+ // Transfer the EObject values into a CDORevision.
object.cdoInternalPostAttach();
+
+ // Remember a copy of the new revision that can be used in case of a later rollback to reset the object.
+ Map<CDOID, CDORevision> attachedRevisions = ((InternalCDOTransaction)object.cdoView()).options().getAttachedRevisionsMap();
+ if (attachedRevisions != null)
+ {
+ InternalCDORevision revision = object.cdoRevision().copy();
+
+ // This revision copy contains the container reference AFTER the attachment.
+ // At rollback time it may have to be unset if this object is the root of an attached tree!
+ attachedRevisions.put(revision.getID(), revision);
+ }
+
changeState(object, CDOState.NEW);
}
}
@@ -1117,8 +1130,38 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
{
public void execute(InternalCDOObject object, CDOState state, CDOEvent event, InternalCDOTransaction transaction)
{
- if (transaction.getLastSavepoint().isNewObject(object.cdoID()))
+ CDOID id = object.cdoID();
+ if (transaction.getLastSavepoint().isNewObject(id))
{
+ // Map<CDOID, CDORevision> attachedRevisions = transaction.options().getAttachedRevisionsMap();
+ // if (attachedRevisions != null)
+ // {
+ // InternalCDORevision revision = (InternalCDORevision)attachedRevisions.get(id);
+ // if (revision != null)
+ // {
+ // CDOID containerID = transaction.provideCDOID(revision.getContainerID());
+ // if (!CDOIDUtil.isNull(containerID) && !attachedRevisions.containsKey(containerID))
+ // {
+ // revision.setContainerID(null);
+ // revision.setContainingFeatureID(0);
+ // }
+ //
+ // CDOID resourceID = revision.getResourceID();
+ // if (!CDOIDUtil.isNull(resourceID) && !attachedRevisions.containsKey(resourceID))
+ // {
+ // revision.setResourceID(null);
+ // }
+ //
+ // object.cdoInternalSetRevision(revision);
+ //
+ // if (object instanceof CDOLegacyWrapper)
+ // {
+ // CDOLegacyWrapper wrapper = (CDOLegacyWrapper)object;
+ // wrapper.cdoInternalPostLoad();
+ // }
+ // }
+ // }
+
// postDetach() requires the object to be TRANSIENT, but listeners must not be notified at this point!
CDOState oldState = setStateQuietely(object, CDOState.TRANSIENT);
object.cdoInternalPostDetach(false);

Back to the top