Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2019-10-31 02:50:12 -0400
committerEike Stepper2019-10-31 02:50:12 -0400
commit44f0bdff5de6c96e4f10a86ebb786c6da2928c1e (patch)
treeb67f8c7f2c84b845948ab7cf0f21696c1bf70297
parentc9c07f7e1cf23236d5dd73ae631d28aeee33d510 (diff)
downloadcdo-44f0bdff5de6c96e4f10a86ebb786c6da2928c1e.tar.gz
cdo-44f0bdff5de6c96e4f10a86ebb786c6da2928c1e.tar.xz
cdo-44f0bdff5de6c96e4f10a86ebb786c6da2928c1e.zip
[552043] Rollback on CDOTransaction does not notify the adapter properly
https://bugs.eclipse.org/bugs/show_bug.cgi?id=552043
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java122
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java4
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java6
3 files changed, 131 insertions, 1 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
new file mode 100644
index 0000000000..a77c3432c4
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_552043_Test.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2019 Eike Stepper (Loehne, 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:
+ * Laurent Redor (Obeo) - initial API and implementation
+ */
+package org.eclipse.emf.cdo.tests.bugzilla;
+
+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.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;
+
+/**
+ * @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
+ {
+ // 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());
+
+ CDOSession session = openSession();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource(getResourcePath("/res"));
+ GenRefMultiContained a = model4Factory.eINSTANCE.createGenRefMultiContained();
+ ContainedElementNoOpposite b = model4Factory.eINSTANCE.createContainedElementNoOpposite();
+ resource.getContents().add(a);
+ resource.getContents().add(b);
+ transaction.commit();
+
+ // Install a cross referencer on the resource.
+ ECrossReferenceAdapter crossReferencer = new ECrossReferenceAdapter();
+ resource.eAdapters().add(crossReferencer);
+
+ RefSingleNonContainedNPL c = model4Factory.eINSTANCE.createRefSingleNonContainedNPL();
+ a.getElements().add(c);
+ c.setElement(b);
+
+ // Ensure that 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.
+ 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);
+
+ // Rollback all the changes until the last successful commit.
+ transaction.rollback();
+
+ @SuppressWarnings("unused")
+ Notification[] aNotifications = aAdapter.getNotifications();
+
+ @SuppressWarnings("unused")
+ Notification[] bNotifications = bAdapter.getNotifications();
+
+ @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.
+ 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());
+ }
+
+ public void testRollbackNewObject() throws Exception
+ {
+ CDOSession session = openSession();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource(getResourcePath("/res"));
+ GenRefMultiContained a = model4Factory.eINSTANCE.createGenRefMultiContained();
+ ContainedElementNoOpposite b = model4Factory.eINSTANCE.createContainedElementNoOpposite();
+ resource.getContents().add(a);
+ resource.getContents().add(b);
+ transaction.commit();
+
+ RefSingleNonContainedNPL c = model4Factory.eINSTANCE.createRefSingleNonContainedNPL();
+ a.getElements().add(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());
+ }
+}
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 900b711701..3bdbadab06 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
@@ -503,12 +503,16 @@ public class CDOObjectImpl extends MinimalEStoreEObjectImpl implements InternalC
{
if (remote)
{
+ assert cdoState() == CDOState.INVALID;
+
eSetDeliver(false);
eBasicSetContainer(null, eContainerFeatureID());
eSetDeliver(true);
return;
}
+ assert cdoState() == CDOState.TRANSIENT;
+
if (TRACER.isEnabled())
{
TRACER.format("Depopulating revision for {0}", this); //$NON-NLS-1$
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 01a437225d..df9815fe29 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
@@ -310,7 +310,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
// If we have an error, we will keep the graph exactly like it was before.
process(object, CDOEvent.DETACH, objectsToDetach);
- // postDetach() requires the object to be TRANSIENT
+ // postDetach() requires the object to be TRANSIENT, but listeners must not be notified at this point!
for (InternalCDOObject content : objectsToDetach)
{
CDOState oldState = setStateQuietely(content, CDOState.TRANSIENT);
@@ -1119,7 +1119,11 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
{
if (transaction.getLastSavepoint().isNewObject(object.cdoID()))
{
+ // 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);
+ setStateQuietely(object, oldState);
+
changeState(object, CDOState.TRANSIENT);
}
else

Back to the top