Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java')
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java270
1 files changed, 161 insertions, 109 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
index 4663bf0f89..e89ce792fb 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java
@@ -28,6 +28,7 @@ import org.eclipse.emf.cdo.common.lock.CDOLockOwner;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDORevision;
@@ -44,7 +45,6 @@ import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
import org.eclipse.emf.cdo.internal.common.commit.FailureCommitInfo;
import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl;
import org.eclipse.emf.cdo.internal.server.bundle.OM;
-import org.eclipse.emf.cdo.server.ContainmentCycleDetectedException;
import org.eclipse.emf.cdo.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
import org.eclipse.emf.cdo.server.IView;
@@ -89,7 +89,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -164,6 +163,8 @@ public class TransactionCommitContext implements InternalCommitContext
private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings);
+ private byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN;
+
private String rollbackMessage;
private List<CDOIDReference> xRefs;
@@ -223,6 +224,11 @@ public class TransactionCommitContext implements InternalCommitContext
return autoReleaseLocksEnabled;
}
+ public byte getRollbackReason()
+ {
+ return rollbackReason;
+ }
+
public String getRollbackMessage()
{
return rollbackMessage;
@@ -414,6 +420,32 @@ public class TransactionCommitContext implements InternalCommitContext
}
}
+ private void applyIDMappings(InternalCDORevision[] revisions, OMMonitor monitor)
+ {
+ try
+ {
+ monitor.begin(revisions.length);
+ for (InternalCDORevision revision : revisions)
+ {
+ if (revision != null)
+ {
+ CDOID newID = idMappings.get(revision.getID());
+ if (newID != null)
+ {
+ revision.setID(newID);
+ }
+
+ revision.adjustReferences(idMapper);
+ monitor.worked();
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
protected void notifyBeforeCommitting(OMMonitor monitor)
{
repository.notifyWriteAccessHandlers(transaction, this, true, monitor.fork());
@@ -573,11 +605,13 @@ public class TransactionCommitContext implements InternalCommitContext
checkXRefs();
monitor.worked();
- if (rollbackMessage == null)
- {
- detachObjects(monitor.fork());
- accessor.write(this, monitor.fork(100));
- }
+ detachObjects(monitor.fork());
+ accessor.write(this, monitor.fork(100));
+ }
+ catch (RollbackException ex)
+ {
+ rollbackReason = ex.getRollbackReason();
+ rollback(ex.getRollbackMessage());
}
catch (Throwable t)
{
@@ -890,9 +924,16 @@ public class TransactionCommitContext implements InternalCommitContext
if (!lockedObjects.isEmpty())
{
- // First lock all objects (incl. possible ref targets).
- // This is a transient operation, it does not check for existance!
- lockManager.lock2(LockType.WRITE, transaction, lockedObjects, 10000);
+ try
+ {
+ // First lock all objects (incl. possible ref targets).
+ // This is a transient operation, it does not check for existance!
+ lockManager.lock2(LockType.WRITE, transaction, lockedObjects, 10000);
+ }
+ catch (Exception ex)
+ {
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_IMPLICIT_LOCKING, ex);
+ }
// If all locks could be acquired, check if locked targets do still exist
if (lockedTargets != null)
@@ -902,8 +943,8 @@ public class TransactionCommitContext implements InternalCommitContext
CDORevision revision = transaction.getRevision(id);
if (revision == null || revision instanceof DetachedCDORevision)
{
- throw new IllegalStateException("Object " + id
- + " can not be referenced anymore because it has been detached");
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, "Attempt by "
+ + transaction + " to introduce a stale reference");
}
}
}
@@ -952,86 +993,6 @@ public class TransactionCommitContext implements InternalCommitContext
}
}
- protected void checkXRefs()
- {
- if (ensuringReferentialIntegrity && detachedObjectTypes != null)
- {
- XRefContext context = new XRefContext();
- xRefs = context.getXRefs(accessor);
- if (!xRefs.isEmpty())
- {
- rollbackMessage = "Referential integrity violated";
- }
- }
- }
-
- protected void checkContainmentCycles()
- {
- if (lastTreeRestructuringCommit == 0)
- {
- // If this was a tree-restructuring commit then lastTreeRestructuringCommit would be initialized.
- return;
- }
-
- if (lastUpdateTime == CDOBranchPoint.UNSPECIFIED_DATE)
- {
- // Happens during replication (see CommitDelegationRequest). Commits are checked in the master repo.
- return;
- }
-
- if (lastTreeRestructuringCommit <= lastUpdateTime)
- {
- // If this client's original state includes the state of the last tree-restructuring commit there's no danger.
- return;
- }
-
- Set<CDOID> objectsThatReachTheRoot = new HashSet<CDOID>();
- for (int i = 0; i < dirtyObjectDeltas.length; i++)
- {
- InternalCDORevisionDelta revisionDelta = dirtyObjectDeltas[i];
- CDOFeatureDelta containerDelta = revisionDelta.getFeatureDelta(CDOContainerFeatureDelta.CONTAINER_FEATURE);
- if (containerDelta != null)
- {
- InternalCDORevision revision = dirtyObjects[i];
- if (!isTheRootReachable(revision, objectsThatReachTheRoot, new HashSet<CDOID>()))
- {
- throw new ContainmentCycleDetectedException("Attempt by " + transaction + " to introduce a containment cycle");
- }
- }
- }
- }
-
- private boolean isTheRootReachable(InternalCDORevision revision, Set<CDOID> objectsThatReachTheRoot,
- Set<CDOID> visited)
- {
- CDOID id = revision.getID();
- if (!visited.add(id))
- {
- // Cycle detected on the way up to the root.
- return false;
- }
-
- if (!objectsThatReachTheRoot.add(id))
- {
- // Has already been checked before.
- return true;
- }
-
- CDOID containerID = (CDOID)revision.getContainerID();
- if (CDOIDUtil.isNull(containerID))
- {
- // The tree root has been reached.
- return true;
- }
-
- // Use this commit context as CDORevisionProvider for the container revisions.
- // This is safe because Repository.commit() serializes all tree-restructuring commits.
- InternalCDORevision containerRevision = getRevision(containerID);
-
- // Recurse Up
- return isTheRootReachable(containerRevision, objectsThatReachTheRoot, visited);
- }
-
private synchronized void unlockObjects()
{
if (!lockedObjects.isEmpty())
@@ -1117,8 +1078,8 @@ public class TransactionCommitContext implements InternalCommitContext
if (oldRevision == null)
{
// If the object is logically locked (see lockObjects) but has a wrong (newer) version, someone else modified it
- throw new ConcurrentModificationException("Attempt by " + transaction + " to modify historical revision: "
- + delta);
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT, "Attempt by " + transaction
+ + " to modify historical revision: " + delta);
}
// Make sure all chunks are loaded
@@ -1131,29 +1092,85 @@ public class TransactionCommitContext implements InternalCommitContext
return newRevision;
}
- private void applyIDMappings(InternalCDORevision[] revisions, OMMonitor monitor)
+ protected void checkContainmentCycles()
{
- try
+ if (lastTreeRestructuringCommit == 0)
{
- monitor.begin(revisions.length);
- for (InternalCDORevision revision : revisions)
+ // If this was a tree-restructuring commit then lastTreeRestructuringCommit would be initialized.
+ return;
+ }
+
+ if (lastUpdateTime == CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ // Happens during replication (see CommitDelegationRequest). Commits are checked in the master repo.
+ return;
+ }
+
+ if (lastTreeRestructuringCommit <= lastUpdateTime)
+ {
+ // If this client's original state includes the state of the last tree-restructuring commit there's no danger.
+ return;
+ }
+
+ Set<CDOID> objectsThatReachTheRoot = new HashSet<CDOID>();
+ for (int i = 0; i < dirtyObjectDeltas.length; i++)
+ {
+ InternalCDORevisionDelta revisionDelta = dirtyObjectDeltas[i];
+ CDOFeatureDelta containerDelta = revisionDelta.getFeatureDelta(CDOContainerFeatureDelta.CONTAINER_FEATURE);
+ if (containerDelta != null)
{
- if (revision != null)
+ InternalCDORevision revision = dirtyObjects[i];
+ if (!isTheRootReachable(revision, objectsThatReachTheRoot, new HashSet<CDOID>()))
{
- CDOID newID = idMappings.get(revision.getID());
- if (newID != null)
- {
- revision.setID(newID);
- }
-
- revision.adjustReferences(idMapper);
- monitor.worked();
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_CONTAINMENT_CYCLE, "Attempt by "
+ + transaction + " to introduce a containment cycle");
}
}
}
- finally
+ }
+
+ private boolean isTheRootReachable(InternalCDORevision revision, Set<CDOID> objectsThatReachTheRoot,
+ Set<CDOID> visited)
+ {
+ CDOID id = revision.getID();
+ if (!visited.add(id))
{
- monitor.done();
+ // Cycle detected on the way up to the root.
+ return false;
+ }
+
+ if (!objectsThatReachTheRoot.add(id))
+ {
+ // Has already been checked before.
+ return true;
+ }
+
+ CDOID containerID = (CDOID)revision.getContainerID();
+ if (CDOIDUtil.isNull(containerID))
+ {
+ // The tree root has been reached.
+ return true;
+ }
+
+ // Use this commit context as CDORevisionProvider for the container revisions.
+ // This is safe because Repository.commit() serializes all tree-restructuring commits.
+ InternalCDORevision containerRevision = getRevision(containerID);
+
+ // Recurse Up
+ return isTheRootReachable(containerRevision, objectsThatReachTheRoot, visited);
+ }
+
+ protected void checkXRefs()
+ {
+ if (ensuringReferentialIntegrity && detachedObjectTypes != null)
+ {
+ XRefContext context = new XRefContext();
+ xRefs = context.getXRefs(accessor);
+ if (!xRefs.isEmpty())
+ {
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY, "Attempt by "
+ + transaction + " to introduce a stale reference");
+ }
}
}
@@ -1438,6 +1455,41 @@ public class TransactionCommitContext implements InternalCommitContext
/**
* @author Eike Stepper
*/
+ private static final class RollbackException extends RuntimeException
+ {
+ private static final long serialVersionUID = 1L;
+
+ private final byte rollbackReason;
+
+ private final String rollbackMessage;
+
+ public RollbackException(byte rollbackReason, String rollbackMessage)
+ {
+ this.rollbackReason = rollbackReason;
+ this.rollbackMessage = rollbackMessage;
+ }
+
+ public RollbackException(byte rollbackReason, Throwable cause)
+ {
+ super(cause);
+ this.rollbackReason = rollbackReason;
+ rollbackMessage = cause.getMessage();
+ }
+
+ public byte getRollbackReason()
+ {
+ return rollbackReason;
+ }
+
+ public String getRollbackMessage()
+ {
+ return rollbackMessage;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
private final class XRefContext implements QueryXRefsContext
{
private Map<EClass, List<EReference>> sourceCandidates = new HashMap<EClass, List<EReference>>();

Back to the top