summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon McDuff2008-05-23 07:41:01 (EDT)
committerSimon McDuff2008-05-23 07:41:01 (EDT)
commit9b9bcfaac140cd53b70c433e6b87823aa7794bde (patch)
treedb212d6a88b489d7634d56887caea8884bc0aa5e
parentf060e6081b32db0db5116ec8eb0b8277af59823d (diff)
downloadcdo-9b9bcfaac140cd53b70c433e6b87823aa7794bde.zip
cdo-9b9bcfaac140cd53b70c433e6b87823aa7794bde.tar.gz
cdo-9b9bcfaac140cd53b70c433e6b87823aa7794bde.tar.bz2
[215688] Create save points
https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java1
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SavePointTest.java269
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOSavePoint.java22
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java7
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSavePointImpl.java124
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java26
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java371
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java14
8 files changed, 753 insertions, 81 deletions
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
index 76551ab..65c647a 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java
@@ -40,6 +40,7 @@ public class AllTests
suite.addTestSuite(RevisionDeltaTest.class);
suite.addTestSuite(IndexReconstructionTest.class);
suite.addTestSuite(NoLegacyTest.class);
+ suite.addTestSuite(SavePointTest.class);
// TODO suite.addTestSuite(GeneratedEcoreTest.class);
// $JUnit-END$
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SavePointTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SavePointTest.java
new file mode 100644
index 0000000..aa3b55a
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SavePointTest.java
@@ -0,0 +1,269 @@
+/***************************************************************************
+ * Copyright (c) 2004 - 2008 Simon McDuff, Canada.
+ * 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.tests;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.CDOSavePoint;
+import org.eclipse.emf.cdo.CDOSession;
+import org.eclipse.emf.cdo.CDOTransaction;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.tests.model1.Category;
+import org.eclipse.emf.cdo.tests.model1.Company;
+import org.eclipse.emf.cdo.tests.model1.Model1Factory;
+import org.eclipse.emf.cdo.tests.model1.Model1Package;
+
+import org.eclipse.emf.internal.cdo.CDOTransactionImpl;
+import org.eclipse.emf.internal.cdo.util.FSMUtil;
+
+import junit.framework.Assert;
+
+/**
+ * @author Simon McDuff
+ */
+public class SavePointTest extends AbstractCDOTest
+{
+ public void testRollbackWithNewObject_Collection() throws Exception
+ {
+
+ CDOSession session = openModel1Session();
+
+ session.getPackageRegistry().putEPackage(Model1Package.eINSTANCE);
+
+ CDOTransaction transaction1 = session.openTransaction();
+ // Client1
+ CDOResource resource1 = transaction1.createResource("/test1");
+
+ Company company1 = Model1Factory.eINSTANCE.createCompany();
+ resource1.getContents().add(company1);
+ Category category1 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category1);
+
+ transaction1.createSavePoint();
+
+ Category category2 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category2);
+
+ CDOSavePoint savePoint2 = transaction1.createSavePoint();
+
+ Category category3 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category3);
+
+ transaction1.createSavePoint();
+
+ transaction1.rollback(savePoint2, false);
+
+ assertEquals(2, company1.getCategories().size());
+
+ transaction1.commit();
+
+ }
+
+ public void testRollbackWithNewObject_Commit() throws Exception
+ {
+ flow1(false, true);
+ }
+ public void testRollbackWithNewObject_Rollback() throws Exception
+ {
+
+ flow1(false, false);
+ }
+
+ public void testRollbackWithPersistedObject_Commit() throws Exception
+ {
+ flow1(true, true);
+ }
+ public void testRollbackWithPersistedObject_Rollback() throws Exception
+ {
+ flow1(true, false);
+ }
+
+ public void testWrongSavePoint() throws Exception
+ {
+ CDOSession session = openModel1Session();
+ session.getPackageRegistry().putEPackage(Model1Package.eINSTANCE);
+
+ CDOTransactionImpl transaction1 = (CDOTransactionImpl)session.openTransaction();
+ // Client1
+ CDOResource resource1 = transaction1.createResource("/test1");
+ Company company1 = Model1Factory.eINSTANCE.createCompany();
+ resource1.getContents().add(company1);
+ Category category1 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category1);
+
+ CDOSavePoint savePoint1 = transaction1.createSavePoint();
+ CDOSavePoint savePoint2 = transaction1.createSavePoint();
+ transaction1.rollback(savePoint1, false);
+ try
+ {
+ transaction1.rollback(savePoint2, false);
+ Assert.assertEquals("Should have thrown an exception", false, true);
+ }
+ catch (IllegalArgumentException illegalArgumentException)
+ {
+
+ }
+ try
+ {
+ transaction1.rollback(null, false);
+ Assert.assertEquals("Should have thrown an exception", false, true);
+ }
+ catch (IllegalArgumentException illegalArgumentException)
+ {
+
+ }
+ }
+ public void testisDirty() throws Exception
+ {
+ CDOSession session = openModel1Session();
+ session.getPackageRegistry().putEPackage(Model1Package.eINSTANCE);
+
+ CDOTransactionImpl transaction1 = (CDOTransactionImpl)session.openTransaction();
+
+ CDOSavePoint savePoint0 = transaction1.createSavePoint();
+ // Client1
+ CDOResource resource1 = transaction1.createResource("/test1");
+ Company company1 = Model1Factory.eINSTANCE.createCompany();
+ resource1.getContents().add(company1);
+ Category category1 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category1);
+
+ CDOSavePoint savePoint1 = transaction1.createSavePoint();
+ Category category2 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category2);
+
+ CDOSavePoint savePoint2 = transaction1.createSavePoint();
+ CDOSavePoint savePoint3 = transaction1.createSavePoint();
+
+ assertEquals(true, transaction1.isDirty());
+
+ transaction1.rollback(savePoint3, false);
+ assertEquals(true, transaction1.isDirty());
+
+ transaction1.rollback(savePoint2, false);
+ assertEquals(true, transaction1.isDirty());
+
+ transaction1.rollback(savePoint1, false);
+ assertEquals(true, transaction1.isDirty());
+
+ // Didn`t make any modification prior to savepoint0
+ transaction1.rollback(savePoint0, false);
+ assertEquals(false, transaction1.isDirty());
+
+ transaction1.rollback(false);
+ assertEquals(false, transaction1.isDirty());
+ }
+ public void flow1(boolean commitBegin, boolean commitEnd) throws Exception
+ {
+ CDOSession session = openModel1Session();
+ session.getPackageRegistry().putEPackage(Model1Package.eINSTANCE);
+
+ CDOTransactionImpl transaction1 = (CDOTransactionImpl)session.openTransaction();
+ // Client1
+ CDOResource resource1 = transaction1.createResource("/test1");
+ Category category3,category2, category4;
+
+ Company company1 = Model1Factory.eINSTANCE.createCompany();
+ resource1.getContents().add(company1);
+ Category category1 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category1);
+
+ CDOSavePoint savePoint1 = transaction1.createSavePoint();
+
+ // Modification for savePoint1
+ Company company2 = Model1Factory.eINSTANCE.createCompany();
+ resource1.getContents().add(company2);
+ company1.setCity("CITY1");
+
+ assertEquals(2, resource1.getContents().size());
+
+ // Rollback
+ transaction1.rollback(savePoint1, false);
+
+ if (commitBegin)
+ transaction1.commit();
+
+
+ {
+ assertEquals(null, company1.getCity());
+ assertEquals(1, resource1.getContents().size());
+ company1.setCity("CITY1");
+ category2 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category2);
+ }
+
+ CDOSavePoint savePoint2 = transaction1.createSavePoint();
+ {
+ company1.setCity("CITY2");
+ category3 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category3);
+ }
+
+
+ transaction1.createSavePoint();
+ {
+ company1.setCity("CITY3");
+ assertEquals(3, company1.getCategories().size());
+ category4 = Model1Factory.eINSTANCE.createCategory();
+ company1.getCategories().add(category4);
+
+ }
+
+ transaction1.rollback(savePoint2, false);
+
+ assertEquals(true , transaction1.isDirty());
+
+ // Test NEW TO NEW
+ assertEquals(false , FSMUtil.isTransient(company1));
+
+ // Test NEW TO TRANSIENT (2 step back)
+ assertEquals(true , FSMUtil.isTransient(category3));
+ assertEquals(false, transaction1.getNewObjects().containsKey(((CDOObject)category3).cdoID()));
+
+ // Test NEW TO TRANSIENT (1 step back)
+ assertEquals(true , FSMUtil.isTransient(category4));
+ assertEquals(false, transaction1.getNewObjects().containsKey(((CDOObject)category4).cdoID()));
+
+ // Test NEW TO NEW
+ assertEquals(false , FSMUtil.isTransient(category2));
+ assertEquals(true, transaction1.getNewObjects().containsKey(((CDOObject)category2).cdoID()));
+
+ // Test rollback NEW
+ assertEquals("CITY1", company1.getCity());
+ assertEquals(2, company1.getCategories().size());
+ if (commitEnd)
+ {
+ transaction1.commit();
+
+ assertClean(company1, transaction1);
+ assertClean(category2, transaction1);
+
+ assertEquals("CITY1", company1.getCity());
+
+ assertEquals(2, company1.getCategories().size());
+
+ assertEquals(null, transaction1.getLastSavePoint().getPreviousSavePoint());
+ }
+ else
+ {
+ transaction1.rollback(false);
+ assertEquals(false , transaction1.isDirty());
+ assertEquals(null , transaction1.getLastSavePoint().getNextSavePoint());
+ assertEquals(null , transaction1.getLastSavePoint().getPreviousSavePoint());
+
+ assertEquals(commitBegin , !FSMUtil.isTransient(company1));
+
+ assertEquals(commitBegin , !FSMUtil.isTransient(resource1));
+
+ }
+
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOSavePoint.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOSavePoint.java
new file mode 100644
index 0000000..d7c69b2
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOSavePoint.java
@@ -0,0 +1,22 @@
+/***************************************************************************
+ * Copyright (c) 2004 - 2008 Simon McDuff, Canada.
+ * 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;
+
+/**
+ * @author Simon McDuff
+ */
+public interface CDOSavePoint
+{
+ CDOTransaction getTransaction();
+ CDOSavePoint getNextSavePoint();
+ CDOSavePoint getPreviousSavePoint();
+
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java
index 34b3f49..1360d9b 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java
@@ -8,6 +8,7 @@
* Contributors:
* Eike Stepper - initial API and implementation
* Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266
+ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688
**************************************************************************/
package org.eclipse.emf.cdo;
@@ -51,6 +52,12 @@ public interface CDOTransaction extends CDOView
public void commit() throws TransactionException;
public void rollback(boolean remote);
+
+ public void rollback(CDOSavePoint savePoint, boolean remote);
+
+ public CDOSavePoint createSavePoint();
+
+ public CDOSavePoint getLastSavePoint();
public void addHandler(CDOTransactionHandler handler);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSavePointImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSavePointImpl.java
new file mode 100644
index 0000000..c3f6b73
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSavePointImpl.java
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * Copyright (c) 2004 - 2008 Simon McDuff, Canada.
+ * 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.internal.cdo;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.CDOSavePoint;
+import org.eclipse.emf.cdo.CDOTransaction;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.internal.protocol.revision.CDORevisionImpl;
+import org.eclipse.emf.cdo.protocol.id.CDOID;
+import org.eclipse.emf.cdo.protocol.revision.delta.CDORevisionDelta;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author Simon McDuff
+ */
+public class CDOSavePointImpl implements CDOSavePoint
+{
+ private CDOTransactionImpl transaction = null;
+
+ private Map<CDOID, CDOResource> newResources = new HashMap<CDOID, CDOResource>();
+
+ private Map<CDOID, CDOObject> newObjects = new HashMap<CDOID, CDOObject>();
+
+ private Map<CDOID, CDORevisionImpl> baseNewObjects = new HashMap<CDOID, CDORevisionImpl>();
+
+ private Map<CDOID, CDOObject> dirtyObjects = new HashMap<CDOID, CDOObject>();
+
+ private ConcurrentMap<CDOID, CDORevisionDelta> revisionDeltas = new ConcurrentHashMap<CDOID, CDORevisionDelta>();
+
+ private CDOSavePointImpl previousSavePoint = null;
+
+ private CDOSavePointImpl nextSavePoint = null;
+
+ private boolean isDirty = false;
+
+ public CDOSavePointImpl(CDOTransactionImpl transaction, CDOSavePointImpl lastSavePoint)
+ {
+ this.transaction = transaction;
+ this.isDirty = transaction.isDirty();
+ this.previousSavePoint = lastSavePoint;
+ if (this.previousSavePoint != null)
+ this.previousSavePoint.setNextSavePoint(this);
+ }
+
+ public void clear()
+ {
+ newResources.clear();
+ newObjects.clear();
+ dirtyObjects.clear();
+ revisionDeltas.clear();
+ baseNewObjects.clear();
+ }
+
+ public boolean isDirty()
+ {
+ return isDirty;
+ }
+
+ public Map<CDOID, CDOResource> getNewResources()
+ {
+ return newResources;
+ }
+
+ public Map<CDOID, CDOObject> getNewObjects()
+ {
+ return newObjects;
+ }
+
+ public Map<CDOID, CDOObject> getDirtyObjects()
+ {
+ return dirtyObjects;
+ }
+
+ public ConcurrentMap<CDOID, CDORevisionDelta> getRevisionDeltas()
+ {
+ return revisionDeltas;
+ }
+
+
+ public Map<CDOID, CDORevisionImpl> getBaseNewObjects()
+ {
+ return baseNewObjects;
+ }
+
+ public CDOSavePointImpl getPreviousSavePoint()
+ {
+ return previousSavePoint;
+ }
+
+ public CDOSavePointImpl getNextSavePoint()
+ {
+ return nextSavePoint;
+ }
+
+ public void setPreviousSavePoint(CDOSavePointImpl previousSavePoint)
+ {
+ this.previousSavePoint = previousSavePoint;
+ }
+
+ public void setNextSavePoint(CDOSavePointImpl nextSavePoint)
+ {
+ this.nextSavePoint = nextSavePoint;
+ }
+
+ public CDOTransaction getTransaction()
+ {
+ return transaction;
+ }
+
+}
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 4e133d1..c7e6acd 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
@@ -8,9 +8,11 @@
* Contributors:
* Eike Stepper - initial API and implementation
* Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266
+ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688
**************************************************************************/
package org.eclipse.emf.internal.cdo;
+import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDORevisionManager;
import org.eclipse.emf.cdo.CDOSession;
import org.eclipse.emf.cdo.CDOState;
@@ -91,7 +93,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
init(CDOState.NEW, CDOEvent.ATTACH, FAIL);
init(CDOState.NEW, CDOEvent.DETACH, new DetachTransition());
init(CDOState.NEW, CDOEvent.READ, IGNORE);
- init(CDOState.NEW, CDOEvent.WRITE, IGNORE);
+ init(CDOState.NEW, CDOEvent.WRITE, new NewObjectWriteTransition());
init(CDOState.NEW, CDOEvent.INVALIDATE, FAIL);
init(CDOState.NEW, CDOEvent.RELOAD, FAIL);
init(CDOState.NEW, CDOEvent.COMMIT, new CommitTransition());
@@ -354,6 +356,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
object.cdoInternalSetID(id);
object.cdoInternalSetResource(data.resource);
object.cdoInternalSetView(data.view);
+
changeState(object, CDOState.PREPARED);
// Create new revision
@@ -385,6 +388,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
{
CDOTransactionImpl transaction = (CDOTransactionImpl)object.cdoView();
object.cdoInternalPostAttach();
+
changeState(object, CDOState.NEW);
// Attach content tree
@@ -494,6 +498,26 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
/**
* @author Simon McDuff
*/
+ private final class NewObjectWriteTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
+ {
+ public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
+ {
+ CDOViewImpl view = (CDOViewImpl)object.cdoView();
+ CDOTransactionImpl transaction = view.toTransaction();
+
+ // Register Delta for new objects only if objectA doesn't belong to this savepoint
+ if (transaction.getLastSavePoint().getPreviousSavePoint() == null || featureDelta == null) return;
+
+ Map<CDOID, ? extends CDOObject> map = object instanceof CDOResource ? transaction.getLastSavePoint()
+ .getNewResources() : transaction.getLastSavePoint().getNewObjects();
+
+ if (!map.containsKey(object.cdoID())) transaction.registerFeatureDelta(object, (CDOFeatureDelta)featureDelta);
+ }
+ }
+
+ /**
+ * @author Simon McDuff
+ */
private final class RewriteTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, Object>
{
public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java
index 17ddf12..f8e4502 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java
@@ -9,10 +9,13 @@
* Eike Stepper - initial API and implementation
* Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266
* Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=233314
+ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688
**************************************************************************/
package org.eclipse.emf.internal.cdo;
import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.CDOSavePoint;
+import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.CDOTransaction;
import org.eclipse.emf.cdo.CDOTransactionConflictEvent;
import org.eclipse.emf.cdo.CDOTransactionFinishedEvent;
@@ -21,12 +24,15 @@ import org.eclipse.emf.cdo.CDOTransactionStartedEvent;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
import org.eclipse.emf.cdo.internal.protocol.model.InternalCDOPackage;
+import org.eclipse.emf.cdo.internal.protocol.revision.CDORevisionImpl;
+import org.eclipse.emf.cdo.internal.protocol.revision.delta.CDORevisionDeltaImpl;
import org.eclipse.emf.cdo.internal.protocol.revision.delta.InternalCDORevisionDelta;
import org.eclipse.emf.cdo.protocol.id.CDOID;
import org.eclipse.emf.cdo.protocol.id.CDOIDTemp;
import org.eclipse.emf.cdo.protocol.id.CDOIDUtil;
import org.eclipse.emf.cdo.protocol.model.CDOPackage;
import org.eclipse.emf.cdo.protocol.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.protocol.revision.delta.CDOListFeatureDelta;
import org.eclipse.emf.cdo.protocol.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.protocol.revision.delta.CDORevisionDeltaUtil;
import org.eclipse.emf.cdo.util.CDOUtil;
@@ -41,6 +47,7 @@ import org.eclipse.net4j.internal.util.event.Notifier;
import org.eclipse.net4j.internal.util.om.trace.ContextTracer;
import org.eclipse.net4j.signal.failover.IFailOverStrategy;
import org.eclipse.net4j.util.ImplementationError;
+import org.eclipse.net4j.util.collection.MultiMap;
import org.eclipse.net4j.util.transaction.TransactionException;
import org.eclipse.emf.common.util.URI;
@@ -51,13 +58,12 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
/**
* @author Eike Stepper
@@ -73,13 +79,9 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
private List<CDOPackage> newPackages;
- private Map<CDOID, CDOResource> newResources = new HashMap<CDOID, CDOResource>();
+ private CDOSavePointImpl lastSavePoint = new CDOSavePointImpl(this, null);
- private Map<CDOID, CDOObject> newObjects = new HashMap<CDOID, CDOObject>();
-
- private Map<CDOID, CDOObject> dirtyObjects = new HashMap<CDOID, CDOObject>();
-
- private ConcurrentMap<CDOID, CDORevisionDelta> revisionDeltas = new ConcurrentHashMap<CDOID, CDORevisionDelta>();
+ private CDOSavePointImpl firstSavePoint = lastSavePoint;
private boolean dirty;
@@ -159,32 +161,6 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
return Collections.unmodifiableList(newPackages);
}
- public Map<CDOID, CDOResource> getNewResources()
- {
- return Collections.unmodifiableMap(newResources);
- }
-
- public Map<CDOID, CDOObject> getNewObjects()
- {
- return Collections.unmodifiableMap(newObjects);
- }
-
- /**
- * TODO Consolidate with {@link #getRevisionDeltas()}
- */
- public Map<CDOID, CDOObject> getDirtyObjects()
- {
- return Collections.unmodifiableMap(dirtyObjects);
- }
-
- /**
- * TODO Consolidate with {@link #getDirtyObjects()}
- */
- public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
- {
- return Collections.unmodifiableMap(revisionDeltas);
- }
-
public CDOIDTemp getNextTemporaryID()
{
return CDOIDUtil.createCDOIDTempObject(++lastTemporaryID);
@@ -209,6 +185,11 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
}
}
+ public CDOSavePointImpl getLastSavePoint()
+ {
+ return this.lastSavePoint;
+ }
+
public void commit() throws TransactionException
{
if (dirty)
@@ -227,8 +208,12 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
{
newPackages = analyzeNewPackages();
- preCommit(newObjects);
- preCommit(dirtyObjects);
+ for (CDOSavePointImpl itrSavePoint = lastSavePoint; itrSavePoint != null; itrSavePoint = itrSavePoint
+ .getPreviousSavePoint())
+ {
+ preCommit(itrSavePoint.getNewObjects());
+ preCommit(itrSavePoint.getDirtyObjects());
+ }
CDOSessionImpl session = getSession();
IChannel channel = session.getChannel();
@@ -242,15 +227,20 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
throw new TransactionException(rollbackMessage);
}
- postCommit(newResources, result);
- postCommit(newObjects, result);
- postCommit(dirtyObjects, result);
-
+ for (CDOSavePointImpl irtSavePoint = lastSavePoint; irtSavePoint != null; irtSavePoint = irtSavePoint
+ .getPreviousSavePoint())
+ {
+ postCommit(irtSavePoint.getNewResources(), result);
+ postCommit(irtSavePoint.getNewObjects(), result);
+ postCommit(irtSavePoint.getDirtyObjects(), result);
+ }
for (CDOPackage newPackage : newPackages)
{
((InternalCDOPackage)newPackage).setPersistent(true);
}
+ Map<CDOID, CDOObject> dirtyObjects = this.getDirtyObjects();
+
if (!dirtyObjects.isEmpty())
{
session.notifyInvalidation(result.getTimeStamp(), dirtyObjects.keySet(), this);
@@ -273,19 +263,36 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
public void rollback(boolean remote)
{
- if (dirty)
+ rollback(firstSavePoint, remote);
+
+ cleanUp();
+ }
+
+ private Set<CDOID> rollbackTo(CDOSavePoint savePoint, boolean remote)
+ {
+ Set<CDOID> newObjectsDelta = new HashSet<CDOID>();
+
+ boolean isActiveSavePoint = false;
+
+ // Start from the last savepoint and come back up to the active
+
+ for (CDOSavePointImpl itrSavePoint = lastSavePoint; itrSavePoint != null; itrSavePoint = itrSavePoint
+ .getPreviousSavePoint())
{
- if (TRACER.isEnabled())
- {
- TRACER.trace("commit()");
- }
- try
+ if (isActiveSavePoint == false)
{
+ // Rollback new objects created after the save point
+ Map<CDOID, CDOResource> newResources = itrSavePoint.getNewResources();
+ Map<CDOID, CDOObject> newObjects = itrSavePoint.getNewObjects();
+
if (!newResources.isEmpty())
{
for (CDOObject newResource : newResources.values())
{
+ // TODO Should call detach transition : not there yet
+ ((InternalCDOObject)newResource).cdoInternalSetState(CDOState.TRANSIENT);
+
removeObject(newResource.cdoID());
getResourceSet().getResources().remove(newResource);
}
@@ -295,38 +302,167 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
{
for (CDOObject newObject : newObjects.values())
{
+ ((InternalCDOObject)newObject).cdoInternalSetState(CDOState.TRANSIENT);
+
removeObject(newObject.cdoID());
+
+ // TODO Should call detach transition : not there yet
+ // TODO How to remove it from Resource?
+ // CDOStateMachine.INSTANCE.detach(newObject);
}
}
- if (!dirtyObjects.isEmpty())
+ Map<CDOID, CDORevisionDelta> revisionDeltas = itrSavePoint.getRevisionDeltas();
+
+ if (!revisionDeltas.isEmpty())
{
- for (CDOObject dirtyObject : dirtyObjects.values())
+ for (CDORevisionDelta dirtyObject : revisionDeltas.values())
{
- CDOStateMachine.INSTANCE.rollback((InternalCDOObject)dirtyObject, remote);
+ if (dirtyObject.getID().isTemporary()) newObjectsDelta.add(dirtyObject.getID());
}
}
+ }
- cleanUp();
-
- Map<CDOIDTemp, CDOID> idMappings = Collections.emptyMap();
-
- fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.ROLLED_BACK, idMappings));
-
- for (CDOTransactionHandler handler : getHandlers())
+ // Rollback every persisted objects
+ Map<CDOID, CDOObject> dirtyObjects = itrSavePoint.getDirtyObjects();
+ if (!dirtyObjects.isEmpty())
+ {
+ for (CDOObject dirtyObject : dirtyObjects.values())
{
- handler.rollingbackTransaction(this);
+ CDOStateMachine.INSTANCE.rollback((InternalCDOObject)dirtyObject, remote);
}
}
- catch (RuntimeException ex)
+ if (savePoint == itrSavePoint) isActiveSavePoint = true;
+ }
+
+ return newObjectsDelta;
+ }
+
+ private void loadSavePoint(CDOSavePoint savePoint, Set<CDOID> newObjectsDelta)
+ {
+ Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
+ Map<CDOID, CDOObject> newObjMaps = getNewObjects();
+
+ Map<CDOID, CDOResource> newResources = getNewResources();
+
+ Map<CDOID, CDORevisionImpl> newBaseRevision = getBaseNewObjects();
+
+ // Reload the objects (NEW) with their base.
+ for (CDOID newObject : newObjectsDelta)
+ {
+ InternalCDOObject object = (InternalCDOObject)newObjMaps.get(newObject);
+ if (object == null) object = (InternalCDOObject)newResources.get(newObject);
+
+ CDORevisionImpl revisionImpl = newBaseRevision.get(newObject);
+
+ if (revisionImpl != null)
{
- throw ex;
+ object.cdoInternalSetRevision(new CDORevisionImpl(revisionImpl));
+
+ // Load the object from revision to EObject
+ object.cdoInternalPostLoad();
}
- catch (Exception ex)
+ }
+
+ for (CDOSavePointImpl itrSavePoint = firstSavePoint; itrSavePoint != savePoint; itrSavePoint = itrSavePoint
+ .getNextSavePoint())
+ {
+ CDOObjectMerger merger = new CDOObjectMerger();
+ for (CDORevisionDelta delta : itrSavePoint.getRevisionDeltas().values())
{
- throw new TransactionException(ex);
+ Map<CDOID, CDOObject> map = delta.getID().isTemporary() ? newObjMaps : dirtyObjects;
+
+ InternalCDOObject object = (InternalCDOObject)map.get(delta.getID());
+ if (object == null) object = (InternalCDOObject)newResources.get(delta.getID());
+
+ // Change state of the objects
+ merger.merge(object, delta);
+
+ // Load the object from revision to EObject
+ object.cdoInternalPostLoad();
}
}
+ dirty = ((CDOSavePointImpl)savePoint).isDirty();
+ }
+
+ public void rollback(CDOSavePoint savePoint, boolean remote)
+ {
+ if (savePoint == null || savePoint.getTransaction() != this)
+ throw new IllegalArgumentException("Save point to rollback doesn't belong to this transaction: " + savePoint);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("commit()");
+ }
+
+ try
+ {
+
+ boolean isPresent = false;
+
+ for (CDOSavePointImpl indexsavePoint = lastSavePoint; indexsavePoint != null; indexsavePoint = indexsavePoint
+ .getPreviousSavePoint())
+ {
+ if (indexsavePoint == savePoint)
+ {
+ isPresent = true;
+ break;
+ }
+ }
+ if (isPresent == false)
+ throw new IllegalArgumentException("Save point to rollback isn't valid for this transaction: " + savePoint);
+
+ // Rollback objects
+ Set<CDOID> newObjectsDelta = rollbackTo(savePoint, remote);
+
+ this.lastSavePoint = (CDOSavePointImpl)savePoint;
+
+ // Make savePoint active. Erase savepoint that could have be after
+ this.lastSavePoint.setNextSavePoint(null);
+ this.lastSavePoint.clear();
+
+ // Load from first savepoint up to current savepoint
+ loadSavePoint(lastSavePoint, newObjectsDelta);
+
+ Map<CDOIDTemp, CDOID> idMappings = Collections.emptyMap();
+
+ fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.ROLLED_BACK, idMappings));
+
+ for (CDOTransactionHandler handler : getHandlers())
+ {
+ handler.rollingbackTransaction(this);
+ }
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ throw new TransactionException(ex);
+ }
+ }
+
+ public CDOSavePoint createSavePoint()
+ {
+ // Take a copy of all new objects for the current save point
+ for (CDOObject object : lastSavePoint.getNewObjects().values())
+ {
+ // Load instance to revision
+ ((InternalCDOObject)object).cdoInternalPreCommit();
+ lastSavePoint.getBaseNewObjects().put(object.cdoID(), new CDORevisionImpl((CDORevisionImpl)object.cdoRevision()));
+ }
+ // Take a copy of all new resources for the current save point
+ for (CDOObject object : lastSavePoint.getNewResources().values())
+ {
+ // Load instance to revision
+ ((InternalCDOObject)object).cdoInternalPreCommit();
+ lastSavePoint.getBaseNewObjects().put(object.cdoID(), new CDORevisionImpl((CDORevisionImpl)object.cdoRevision()));
+ }
+
+ lastSavePoint = new CDOSavePointImpl(this, lastSavePoint);
+
+ return lastSavePoint;
}
@Override
@@ -349,24 +485,25 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
if (object instanceof CDOResourceImpl)
{
- register(newResources, object);
+ register(this.lastSavePoint.getNewResources(), object);
}
else
{
- register(newObjects, object);
+ register(this.lastSavePoint.getNewObjects(), object);
}
}
public void registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta)
{
- InternalCDORevisionDelta revisionDelta = (InternalCDORevisionDelta)revisionDeltas.get(object.cdoID());
+ CDORevisionDelta revisionDelta = (CDORevisionDelta)lastSavePoint.getRevisionDeltas().get(object.cdoID());
+
if (revisionDelta == null)
{
- revisionDelta = (InternalCDORevisionDelta)CDORevisionDeltaUtil.create(object.cdoRevision());
- revisionDeltas.put(object.cdoID(), revisionDelta);
+ revisionDelta = (CDORevisionDelta)CDORevisionDeltaUtil.create(object.cdoRevision());
+ lastSavePoint.getRevisionDeltas().put(object.cdoID(), revisionDelta);
}
- revisionDelta.addFeatureDelta(featureDelta);
+ ((InternalCDORevisionDelta)revisionDelta).addFeatureDelta(featureDelta);
for (CDOTransactionHandler handler : getHandlers())
{
handler.modifyingObject(this, object, featureDelta);
@@ -375,7 +512,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
public void registerRevisionDelta(CDORevisionDelta revisionDelta)
{
- revisionDeltas.putIfAbsent(revisionDelta.getID(), revisionDelta);
+ lastSavePoint.getRevisionDeltas().putIfAbsent(revisionDelta.getID(), revisionDelta);
}
public void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta)
@@ -386,7 +523,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
}
registerFeatureDelta(object, featureDelta);
- register(dirtyObjects, object);
+ register(lastSavePoint.getDirtyObjects(), object);
}
@SuppressWarnings("unchecked")
@@ -409,7 +546,7 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
{
// Find all used classes and their super classes
Set<EClass> usedClasses = new HashSet<EClass>();
- for (CDOObject object : newObjects.values())
+ for (CDOObject object : getNewObjects().values())
{
EClass usedClass = object.eClass();
if (usedClasses.add(usedClass))
@@ -486,15 +623,101 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction
private void cleanUp()
{
newPackages = null;
- newResources.clear();
- newObjects.clear();
- dirtyObjects.clear();
- revisionDeltas.clear();
+ lastSavePoint = firstSavePoint;
+ firstSavePoint.clear();
+ firstSavePoint.setNextSavePoint(null);
dirty = false;
conflict = false;
lastTemporaryID = 0;
}
+ public Map<CDOID, CDOObject> getDirtyObjects()
+ {
+ if (this.lastSavePoint.getPreviousSavePoint() == null) return lastSavePoint.getDirtyObjects();
+
+ MultiMap.ListBased<CDOID, CDOObject> dirtyObjects = new MultiMap.ListBased<CDOID, CDOObject>();
+
+ for (CDOSavePointImpl savePoint = lastSavePoint; savePoint != null; savePoint = savePoint.getPreviousSavePoint())
+ {
+ dirtyObjects.getDelegates().add(savePoint.getDirtyObjects());
+ }
+ return dirtyObjects;
+ }
+
+ public Map<CDOID, CDOObject> getNewObjects()
+ {
+ if (this.lastSavePoint.getPreviousSavePoint() == null)
+ return Collections.unmodifiableMap(lastSavePoint.getNewObjects());
+
+ MultiMap.ListBased<CDOID, CDOObject> newObjects = new MultiMap.ListBased<CDOID, CDOObject>();
+ for (CDOSavePointImpl savePoint = lastSavePoint; savePoint != null; savePoint = savePoint.getPreviousSavePoint())
+ {
+ newObjects.getDelegates().add(savePoint.getNewObjects());
+ }
+ return newObjects;
+ }
+
+ public Map<CDOID, CDOResource> getNewResources()
+ {
+ if (this.lastSavePoint.getPreviousSavePoint() == null)
+ return Collections.unmodifiableMap(lastSavePoint.getNewResources());
+
+ MultiMap.ListBased<CDOID, CDOResource> newResources = new MultiMap.ListBased<CDOID, CDOResource>();
+ for (CDOSavePointImpl savePoint = lastSavePoint; savePoint != null; savePoint = savePoint.getPreviousSavePoint())
+ {
+ newResources.getDelegates().add(savePoint.getNewResources());
+ }
+ return newResources;
+ }
+
+ public Map<CDOID, CDORevisionImpl> getBaseNewObjects()
+ {
+ if (this.lastSavePoint.getPreviousSavePoint() == null)
+ return Collections.unmodifiableMap(lastSavePoint.getBaseNewObjects());
+
+ MultiMap.ListBased<CDOID, CDORevisionImpl> newObjects = new MultiMap.ListBased<CDOID, CDORevisionImpl>();
+ for (CDOSavePointImpl savePoint = lastSavePoint; savePoint != null; savePoint = savePoint.getPreviousSavePoint())
+ {
+ newObjects.getDelegates().add(savePoint.getBaseNewObjects());
+ }
+ return newObjects;
+ }
+
+ public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
+ {
+ if (this.lastSavePoint.getPreviousSavePoint() == null) return lastSavePoint.getRevisionDeltas();
+
+ // We need to combined the result for all delta in different SavePoint
+ Map<CDOID, CDORevisionDelta> revisionDeltas = new ConcurrentHashMap<CDOID, CDORevisionDelta>();
+
+ for (CDOSavePointImpl savePoint = firstSavePoint; savePoint != null; savePoint = savePoint.getNextSavePoint())
+ {
+ for (Entry<CDOID, CDORevisionDelta> entry : savePoint.getRevisionDeltas().entrySet())
+ {
+ // Skipping temporary
+ if (entry.getKey().isTemporary()) continue;
+
+ CDORevisionDeltaImpl revisionDelta = (CDORevisionDeltaImpl)revisionDeltas.get(entry.getKey());
+ if (revisionDelta == null)
+ revisionDeltas.put(entry.getKey(), entry.getValue());
+ else
+ {
+ for (CDOFeatureDelta delta : entry.getValue().getFeatureDeltas())
+ {
+ if (delta instanceof CDOListFeatureDelta)
+ {
+ for (CDOFeatureDelta subDelta : ((CDOListFeatureDelta)delta).getListChanges())
+ revisionDelta.addFeatureDelta(subDelta);
+ }
+ else
+ revisionDelta.addFeatureDelta(delta);
+ }
+ }
+ }
+ }
+ return revisionDeltas;
+ }
+
/**
* @author Eike Stepper
*/
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java
index c09232a..d58c75d 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java
@@ -8,6 +8,7 @@
* Contributors:
* Eike Stepper - initial API and implementation
* Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266
+ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688
**************************************************************************/
package org.eclipse.emf.internal.cdo.protocol;
@@ -39,6 +40,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
/**
* @author Eike Stepper
@@ -67,12 +69,12 @@ public class CommitTransactionRequest extends CDOClientRequest<CommitTransaction
List<CDOPackage> newPackages = transaction.getNewPackages();
Collection<CDOResource> newResources = transaction.getNewResources().values();
Collection<CDOObject> newObjects = transaction.getNewObjects().values();
- Collection<CDORevisionDelta> dirtyObjects = transaction.getRevisionDeltas().values();
+ Collection<CDORevisionDelta> revisionDeltas = transaction.getRevisionDeltas().values();
out.writeInt(transaction.getViewID());
out.writeInt(newPackages.size());
out.writeInt(newResources.size() + newObjects.size());
- out.writeInt(dirtyObjects.size());
+ out.writeInt(revisionDeltas.size());
if (PROTOCOL.isEnabled())
{
@@ -99,14 +101,14 @@ public class CommitTransactionRequest extends CDOClientRequest<CommitTransaction
if (PROTOCOL.isEnabled())
{
- PROTOCOL.format("Writing {0} dirty objects", dirtyObjects.size());
+ PROTOCOL.format("Writing {0} dirty objects", revisionDeltas.size());
}
-
+ Map<CDOID, CDOObject> dirtyObjects = transaction.getDirtyObjects();
RevisionAdjuster revisionAdjuster = new RevisionAdjuster(transaction);
- for (CDORevisionDelta revisionDelta : dirtyObjects)
+ for (CDORevisionDelta revisionDelta : revisionDeltas)
{
revisionDelta.write(out, transaction);
- CDOObject object = transaction.getDirtyObjects().get(revisionDelta.getID());
+ CDOObject object = dirtyObjects.get(revisionDelta.getID());
InternalCDORevision revision = (InternalCDORevision)object.cdoRevision();
revisionAdjuster.adjustRevision(revision, revisionDelta);
}