From da6dfefe10ed83dc74009c7424832d4d6f91cb7b Mon Sep 17 00:00:00 2001 From: Caspar De Groot Date: Mon, 18 Jan 2010 10:09:01 +0000 Subject: [298561] Uncommitted dangling references not removed on passiveUpdate/refresh https://bugs.eclipse.org/bugs/show_bug.cgi?id=298561 --- .../eclipse/emf/cdo/tests/AllTestsAllConfigs.java | 4 +- .../cdo/tests/bugzilla/Bugzilla_298561_Test.java | 233 +++++++++++++++++++++ .../cdo/transaction/CDOTransactionImpl.java | 42 ++++ 3 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_298561_Test.java (limited to 'plugins') diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java index 27fb76ef3d..0676fe589f 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java @@ -63,6 +63,7 @@ import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_289984_Test; import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_292372_Test; import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_294850_Test; import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_294859_Test; +import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_298561_Test; import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_299190_Test; import org.eclipse.emf.cdo.tests.config.impl.ConfigTest; import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite; @@ -180,8 +181,9 @@ public abstract class AllTestsAllConfigs extends ConfigTestSuite testClasses.add(Bugzilla_294850_Test.class); testClasses.add(Bugzilla_294859_Test.class); testClasses.add(Bugzilla_292372_Test.class); + testClasses.add(Bugzilla_298561_Test.class); testClasses.add(Bugzilla_299190_Test.class); - + // TODO testClasses.add(NonCDOResourceTest.class); // TODO testClasses.add(GeneratedEcoreTest.class); } diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_298561_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_298561_Test.java new file mode 100644 index 0000000000..454ae099cb --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_298561_Test.java @@ -0,0 +1,233 @@ +package org.eclipse.emf.cdo.tests.bugzilla; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOState; +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.RefMultiNonContainedNPL; +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.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.ObjectNotFoundException; + +import org.eclipse.emf.ecore.util.EcoreUtil; + +public class Bugzilla_298561_Test extends AbstractCDOTest +{ + private static String RESOURCENAME = "/r1"; + + public void test_new() + { + CDOSession session = openSession(); + session.options().setPassiveUpdateEnabled(false); + session.getPackageRegistry().putEPackage(model4Package.eINSTANCE); + + CDOTransaction tx = session.openTransaction(); + CDOResource r1 = tx.createResource(RESOURCENAME); + + // Create referencee and store it + ContainedElementNoOpposite referencee = model4Factory.eINSTANCE.createContainedElementNoOpposite(); + r1.getContents().add(referencee); + tx.commit(); + + // Create referencer, don't store it -- keep it as NEW + RefSingleNonContainedNPL referencer = model4Factory.eINSTANCE.createRefSingleNonContainedNPL(); + r1.getContents().add(referencer); + referencer.setElement(referencee); + assertEquals(CDOState.NEW, ((CDOObject)referencer).cdoState()); + + // Delete the referencee in 2nd session + doSecondSession(); + + // Refresh + session.refresh(); + + // + try + { + boolean isSet = referencer.eIsSet(model4Package.eINSTANCE.getRefSingleNonContainedNPL_Element()); + System.out.println("---> " + isSet); + + if (isSet) + { + ContainedElementNoOpposite e = referencer.getElement(); + System.out.println("---> " + e); + } + } + catch (ObjectNotFoundException e) + { + fail("Should not have thrown ObjectNotFoundException"); + } + + tx.close(); + session.close(); + } + + public void test_dirty() + { + CDOSession session = openSession(); + session.options().setPassiveUpdateEnabled(false); + session.getPackageRegistry().putEPackage(model4Package.eINSTANCE); + + CDOTransaction tx = session.openTransaction(); + CDOResource r1 = tx.createResource(RESOURCENAME); + + // Create referencee and store it + ContainedElementNoOpposite referencee = model4Factory.eINSTANCE.createContainedElementNoOpposite(); + r1.getContents().add(referencee); + tx.commit(); + + System.out.println("---> " + ((CDOObject)referencee).cdoID()); + + // Create referencer, store it, then make it DIRTY + RefSingleNonContainedNPL referencer = model4Factory.eINSTANCE.createRefSingleNonContainedNPL(); + r1.getContents().add(referencer); + referencer.setElement(referencee); + tx.commit(); + referencer.setElement(null); + referencer.setElement(referencee); + assertEquals(CDOState.DIRTY, ((CDOObject)referencer).cdoState()); + + // Delete the referencee in 2nd session + doSecondSession(); + + // Refresh + session.refresh(); + + // + try + { + boolean isSet = referencer.eIsSet(model4Package.eINSTANCE.getRefSingleNonContainedNPL_Element()); + System.out.println("---> " + isSet); + + if (isSet) + { + ContainedElementNoOpposite e = referencer.getElement(); + System.out.println("---> " + e); + } + } + catch (ObjectNotFoundException e) + { + fail("Should not have thrown ObjectNotFoundException"); + } + + tx.close(); + session.close(); + } + + private void doSecondSession() + { + CDOSession session = openModel1Session(); + CDOTransaction tx = session.openTransaction(); + CDOResource r1 = tx.getResource(RESOURCENAME); + ContainedElementNoOpposite referencee = (ContainedElementNoOpposite)r1.getContents().get(0); + EcoreUtil.delete(referencee); + tx.commit(); + tx.close(); + session.close(); + } + + public void test_new_multi() + { + CDOSession session = openSession(); + session.options().setPassiveUpdateEnabled(false); + session.getPackageRegistry().putEPackage(model4Package.eINSTANCE); + + CDOTransaction tx = session.openTransaction(); + CDOResource r1 = tx.createResource(RESOURCENAME); + + // Create referencee and store it + ContainedElementNoOpposite referencee = model4Factory.eINSTANCE.createContainedElementNoOpposite(); + r1.getContents().add(referencee); + tx.commit(); + + System.out.println("---> " + ((CDOObject)referencee).cdoID()); + + // Create referencer, don't store it -- keep it as NEW + RefMultiNonContainedNPL referencer = model4Factory.eINSTANCE.createRefMultiNonContainedNPL(); + r1.getContents().add(referencer); + referencer.getElements().add(referencee); + assertEquals(CDOState.NEW, ((CDOObject)referencer).cdoState()); + + // Delete the referencee in 2nd session + doSecondSession(); + + // Refresh + session.refresh(); + + // + try + { + boolean isSet = referencer.eIsSet(model4Package.eINSTANCE.getRefMultiNonContainedNPL_Elements()); + System.out.println("---> " + isSet); + + if (isSet && referencer.getElements().size() > 0) + { + ContainedElementNoOpposite e = referencer.getElements().get(0); + System.out.println("---> " + e); + } + } + catch (ObjectNotFoundException e) + { + fail("Should not have thrown ObjectNotFoundException"); + } + + tx.close(); + session.close(); + } + + public void test_dirty_multi() + { + CDOSession session = openSession(); + session.options().setPassiveUpdateEnabled(false); + session.getPackageRegistry().putEPackage(model4Package.eINSTANCE); + + CDOTransaction tx = session.openTransaction(); + CDOResource r1 = tx.createResource(RESOURCENAME); + + // Create referencee and store it + ContainedElementNoOpposite referencee = model4Factory.eINSTANCE.createContainedElementNoOpposite(); + r1.getContents().add(referencee); + tx.commit(); + + System.out.println("---> " + ((CDOObject)referencee).cdoID()); + + // Create referencer, store it, then make it DIRTY + RefMultiNonContainedNPL referencer = model4Factory.eINSTANCE.createRefMultiNonContainedNPL(); + r1.getContents().add(referencer); + referencer.getElements().add(referencee); + tx.commit(); + referencer.getElements().remove(referencee); + referencer.getElements().add(referencee); + assertEquals(CDOState.DIRTY, ((CDOObject)referencer).cdoState()); + + // Delete the referencee in 2nd session + doSecondSession(); + + // Refresh + session.refresh(); + + // + try + { + boolean isSet = referencer.eIsSet(model4Package.eINSTANCE.getRefMultiNonContainedNPL_Elements()); + System.out.println("---> " + isSet); + + if (isSet && referencer.getElements().size() > 0) + { + ContainedElementNoOpposite e = referencer.getElements().get(0); + System.out.println("---> " + e); + } + } + catch (ObjectNotFoundException e) + { + fail("Should not have thrown ObjectNotFoundException"); + } + + tx.close(); + session.close(); + } +} 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 7e889328d9..8cc3e29f1b 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 @@ -77,7 +77,14 @@ import org.eclipse.net4j.util.options.OptionsEvent; import org.eclipse.net4j.util.transaction.TransactionException; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.util.EContentsEList; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; import org.eclipse.emf.spi.cdo.CDOTransactionStrategy; import org.eclipse.emf.spi.cdo.InternalCDOObject; import org.eclipse.emf.spi.cdo.InternalCDOSavepoint; @@ -1580,6 +1587,41 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa super.doDeactivate(); } + /** + * @since 3.0 + */ + @Override + public Set handleInvalidationWithoutNotification(Set dirtyOIDs, + Collection detachedOIDs, Set dirtyObjects, Set detachedObjects) + { + // Bugzilla 298561: This override removes references to remotely + // detached objects that are present in any DIRTY or NEW objects + + removeCrossReferences(getDirtyObjects().values(), detachedOIDs); + removeCrossReferences(getNewObjects().values(), detachedOIDs); + + return super.handleInvalidationWithoutNotification(dirtyOIDs, detachedOIDs, dirtyObjects, detachedObjects); + } + + private void removeCrossReferences(Collection objects, Collection referencedOIDs) + { + for (CDOObject object : objects) + { + for (EContentsEList.FeatureIterator it = (FeatureIterator)object.eCrossReferences().iterator(); it + .hasNext();) + { + EObject crossReferencedObject = it.next(); + if (crossReferencedObject instanceof CDOObject + && referencedOIDs.contains(((CDOObject)crossReferencedObject).cdoID())) + { + EReference eReference = (EReference)it.feature(); + Setting setting = ((InternalEObject)object).eSetting(eReference); + EcoreUtil.remove(setting, crossReferencedObject); + } + } + } + } + /** * @author Simon McDuff */ -- cgit v1.2.3