[409574] Provide a meaningful CommitException hierarchy 
https://bugs.eclipse.org/bugs/show_bug.cgi?id=409574
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
index f7cd89d..ee540b9 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java
@@ -339,4 +339,29 @@
    * @since 4.1
    */
   public static final byte REPLICATE_LOCKAREA = 3;
+
+  /**
+   * @since 4.2
+   */
+  public static final byte ROLLBACK_REASON_UNKNOWN = 0;
+
+  /**
+   * @since 4.2
+   */
+  public static final byte ROLLBACK_REASON_IMPLICIT_LOCKING = 1;
+
+  /**
+   * @since 4.2
+   */
+  public static final byte ROLLBACK_REASON_COMMIT_CONFLICT = 2;
+
+  /**
+   * @since 4.2
+   */
+  public static final byte ROLLBACK_REASON_CONTAINMENT_CYCLE = 3;
+
+  /**
+   * @since 4.2
+   */
+  public static final byte ROLLBACK_REASON_REFERENTIAL_INTEGRITY = 4;
 }
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
index 6c8faad..d983875 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java
@@ -304,8 +304,8 @@
     boolean success = in.readBoolean();
     if (!success)
     {
+      byte rollbackReason = in.readByte();
       String rollbackMessage = in.readString();
-      OM.LOG.error(rollbackMessage);
 
       CDOBranchPoint branchPoint = in.readCDOBranchPoint();
       long previousTimeStamp = in.readLong();
@@ -322,8 +322,8 @@
         }
       }
 
-      return new CommitTransactionResult(idProvider, rollbackMessage, branchPoint, previousTimeStamp, xRefs,
-          clearResourcePathCache);
+      return new CommitTransactionResult(idProvider, rollbackReason, rollbackMessage, branchPoint, previousTimeStamp,
+          xRefs, clearResourcePathCache);
     }
 
     return null;
diff --git a/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java
index 0a0e4f6..0b9dfad 100644
--- a/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java
+++ b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java
@@ -18,6 +18,7 @@
 import org.eclipse.emf.cdo.common.id.CDOIDReference;
 import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 import org.eclipse.emf.cdo.common.lock.CDOLockState;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.server.IView;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
@@ -212,6 +213,11 @@
     return null;
   }
 
+  public byte getRollbackReason()
+  {
+    return CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN;
+  }
+
   public String getRollbackMessage()
   {
     return null;
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
index 5f214fb..b54ce5e 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java
@@ -322,7 +322,11 @@
 
     try
     {
-      success = respondingException(out, commitContext.getRollbackMessage(), commitContext.getXRefs());
+      byte rollbackReason = commitContext.getRollbackReason();
+      String rollbackMessage = commitContext.getRollbackMessage();
+      List<CDOIDReference> xRefs = commitContext.getXRefs();
+
+      success = respondingException(out, rollbackReason, rollbackMessage, xRefs);
       if (success)
       {
         respondingResult(out);
@@ -336,13 +340,14 @@
     }
   }
 
-  protected boolean respondingException(CDODataOutput out, String rollbackMessage, List<CDOIDReference> xRefs)
-      throws Exception
+  protected boolean respondingException(CDODataOutput out, byte rollbackReason, String rollbackMessage,
+      List<CDOIDReference> xRefs) throws Exception
   {
     boolean success = rollbackMessage == null;
     out.writeBoolean(success);
     if (!success)
     {
+      out.writeByte(rollbackReason);
       out.writeString(rollbackMessage);
       out.writeCDOBranchPoint(commitContext.getBranchPoint());
       out.writeLong(commitContext.getPreviousTimeStamp());
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionCancelIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionCancelIndication.java
index f05e687..a1a3086 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionCancelIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionCancelIndication.java
@@ -37,6 +37,7 @@
   protected void responding(CDODataOutput out, OMMonitor monitor) throws Exception
   {
     String exceptionMessage = null;
+
     try
     {
       if (commitContext != null)
@@ -54,7 +55,7 @@
       exceptionMessage = commitContext.getRollbackMessage();
     }
 
-    respondingException(out, exceptionMessage, null);
+    respondingException(out, CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, exceptionMessage, null);
   }
 
   @Override
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase1Indication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase1Indication.java
index 817cd34..f70b26c 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase1Indication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase1Indication.java
@@ -63,7 +63,7 @@
       exceptionMessage = commitContext.getRollbackMessage();
     }
 
-    boolean success = respondingException(out, exceptionMessage, null);
+    boolean success = respondingException(out, CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, exceptionMessage, null);
     if (success)
     {
       respondingResult(out);
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase2Indication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase2Indication.java
index a0400b5..caaf0e7 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase2Indication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase2Indication.java
@@ -86,7 +86,7 @@
       exceptionMessage = commitContext.getRollbackMessage();
     }
 
-    respondingException(out, exceptionMessage, null);
+    respondingException(out, CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, exceptionMessage, null);
   }
 
   @Override
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase3Indication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase3Indication.java
index eed8b06..1db1d6a 100644
--- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase3Indication.java
+++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitXATransactionPhase3Indication.java
@@ -37,7 +37,8 @@
   protected void responding(CDODataOutput out, OMMonitor monitor) throws Exception
   {
     commitContext.commit(monitor);
-    boolean success = respondingException(out, commitContext.getRollbackMessage(), null);
+    boolean success = respondingException(out, CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN,
+        commitContext.getRollbackMessage(), null);
     commitContext.postCommit(success);
   }
 
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java
index fa50bd9..0b7c6dc 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/DelegatingCommitContext.java
@@ -11,18 +11,27 @@
 package org.eclipse.emf.cdo.internal.server;
 
 import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
 import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDReference;
+import org.eclipse.emf.cdo.common.lock.CDOLockState;
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.server.IStoreAccessor;
 import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
 import org.eclipse.emf.cdo.server.ITransaction;
+import org.eclipse.emf.cdo.server.IView;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
 import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
 import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
 
+import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
+import org.eclipse.net4j.util.io.ExtendedDataInputStream;
+
 import org.eclipse.emf.ecore.EClass;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -102,8 +111,68 @@
     return getDelegate().getIDMappings();
   }
 
+  public long getPreviousTimeStamp()
+  {
+    return getDelegate().getPreviousTimeStamp();
+  }
+
+  public long getLastUpdateTime()
+  {
+    return getDelegate().getLastUpdateTime();
+  }
+
+  public boolean isClearResourcePathCache()
+  {
+    return getDelegate().isClearResourcePathCache();
+  }
+
+  public boolean isUsingEcore()
+  {
+    return getDelegate().isUsingEcore();
+  }
+
+  public boolean isUsingEtypes()
+  {
+    return getDelegate().isUsingEtypes();
+  }
+
+  public CDOLockState[] getLocksOnNewObjects()
+  {
+    return getDelegate().getLocksOnNewObjects();
+  }
+
+  public CDOBranchVersion[] getDetachedObjectVersions()
+  {
+    return getDelegate().getDetachedObjectVersions();
+  }
+
+  public ExtendedDataInputStream getLobs()
+  {
+    return getDelegate().getLobs();
+  }
+
+  public CDOCommitInfo createCommitInfo()
+  {
+    return getDelegate().createCommitInfo();
+  }
+
+  public List<LockState<Object, IView>> getPostCommmitLockStates()
+  {
+    return getDelegate().getPostCommmitLockStates();
+  }
+
+  public byte getRollbackReason()
+  {
+    return getDelegate().getRollbackReason();
+  }
+
   public String getRollbackMessage()
   {
     return getDelegate().getRollbackMessage();
   }
+
+  public List<CDOIDReference> getXRefs()
+  {
+    return getDelegate().getXRefs();
+  }
 }
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 4663bf0..e89ce79 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.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.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.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 @@
 
   private CDOReferenceAdjuster idMapper = new CDOIDMapper(idMappings);
 
+  private byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN;
+
   private String rollbackMessage;
 
   private List<CDOIDReference> xRefs;
@@ -223,6 +224,11 @@
     return autoReleaseLocksEnabled;
   }
 
+  public byte getRollbackReason()
+  {
+    return rollbackReason;
+  }
+
   public String getRollbackMessage()
   {
     return rollbackMessage;
@@ -414,6 +420,32 @@
     }
   }
 
+  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 @@
       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 @@
 
       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 @@
             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 @@
     }
   }
 
-  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 @@
     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 @@
     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 (revision != null)
-        {
-          CDOID newID = idMappings.get(revision.getID());
-          if (newID != null)
-          {
-            revision.setID(newID);
-          }
+      // If this was a tree-restructuring commit then lastTreeRestructuringCommit would be initialized.
+      return;
+    }
 
-          revision.adjustReferences(idMapper);
-          monitor.worked();
+    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 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 @@
   /**
    * @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>>();
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java
index d09083d..4ae413f 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java
@@ -425,6 +425,12 @@
     public CDOCommitInfo createCommitInfo();
 
     /**
+     * @see CDOProtocolConstants
+     * @since 4.2
+     */
+    public byte getRollbackReason();
+
+    /**
      * @since 3.0
      */
     public String getRollbackMessage();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_409284_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_409284_Test.java
index d4fdb4a..bb7ab7d 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_409284_Test.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_409284_Test.java
@@ -16,7 +16,7 @@
 import org.eclipse.emf.cdo.tests.model1.Category;
 import org.eclipse.emf.cdo.tests.model1.Company;
 import org.eclipse.emf.cdo.transaction.CDOTransaction;
-import org.eclipse.emf.cdo.util.CommitException;
+import org.eclipse.emf.cdo.util.ContainmentCycleException;
 
 import org.eclipse.emf.common.util.EList;
 
@@ -77,12 +77,11 @@
     try
     {
       transaction2.commit();
-      fail("CommitException expected");
+      fail("ContainmentCycleException expected");
     }
-    catch (CommitException expected)
+    catch (ContainmentCycleException expected)
     {
-      String message = expected.getMessage();
-      assertEquals(true, message.contains("ContainmentCycleDetectedException"));
+      // SUCCESS
     }
   }
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitConflictException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitConflictException.java
new file mode 100644
index 0000000..3b33842
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitConflictException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2004-2013 Eike Stepper (Berlin, 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class CommitConflictException extends ConcurrentAccessException
+{
+  private static final long serialVersionUID = 1L;
+
+  public CommitConflictException(String message)
+  {
+    super(message);
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitException.java
index 643e44c..ed1fc42 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitException.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitException.java
@@ -15,7 +15,7 @@
 /**
  * A checked exception being thrown from {@link CDOTransaction#commit()} in case of unrecoverable commit problems such
  * as commit conflicts.
- * 
+ *
  * @author Eike Stepper
  * @since 3.0
  * @noextend This interface is not intended to be extended by clients.
@@ -43,4 +43,20 @@
   {
     super(message, cause);
   }
+
+  /**
+   * @since 4.2
+   */
+  public boolean isLocal()
+  {
+    return false;
+  }
+
+  /**
+   * @since 4.2
+   */
+  public boolean isFatal()
+  {
+    return true;
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitIntegrityException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitIntegrityException.java
index e5d07f0..121398a 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitIntegrityException.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/CommitIntegrityException.java
@@ -19,17 +19,17 @@
 /**
  * A {@link CommitException commit exception} that indicates referential integrity problems with
  * {@link CDOTransaction#setCommittables(Set) partial commits} before the server is contacted.
- * 
+ *
  * @author Caspar De Groot
  * @since 4.0
  * @noextend This interface is not intended to be extended by clients.
  * @noinstantiate This class is not intended to be instantiated by clients.
  */
-public class CommitIntegrityException extends CommitException
+public class CommitIntegrityException extends DataIntegrityException
 {
   private static final long serialVersionUID = 1L;
 
-  private Set<? extends EObject> missingObjects;
+  private transient Set<? extends EObject> missingObjects;
 
   public CommitIntegrityException(String msg, Set<? extends EObject> missingObjects)
   {
@@ -41,4 +41,10 @@
   {
     return missingObjects;
   }
+
+  @Override
+  public boolean isLocal()
+  {
+    return true;
+  }
 }
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ConcurrentAccessException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ConcurrentAccessException.java
new file mode 100644
index 0000000..c6cc6e7
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ConcurrentAccessException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010-2012 Eike Stepper (Berlin, 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:
+ *    Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class ConcurrentAccessException extends CommitException
+{
+  private static final long serialVersionUID = 1L;
+
+  public ConcurrentAccessException()
+  {
+  }
+
+  public ConcurrentAccessException(String message, Throwable cause)
+  {
+    super(message, cause);
+  }
+
+  public ConcurrentAccessException(String message)
+  {
+    super(message);
+  }
+
+  public ConcurrentAccessException(Throwable cause)
+  {
+    super(cause);
+  }
+
+  @Override
+  public boolean isFatal()
+  {
+    return false;
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ContainmentCycleException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ContainmentCycleException.java
new file mode 100644
index 0000000..efbb388
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ContainmentCycleException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2004-2013 Eike Stepper (Berlin, 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class ContainmentCycleException extends ConcurrentAccessException
+{
+  private static final long serialVersionUID = 1L;
+
+  public ContainmentCycleException(String message)
+  {
+    super(message);
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingIntegrityException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingIntegrityException.java
new file mode 100644
index 0000000..2f10f9d
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingIntegrityException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008, 2009, 2011, 2012 Eike Stepper (Berlin, 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:
+ *    Simon McDuff - initial API and implementation
+ *    Eike Stepper - maintenance
+ */
+package org.eclipse.emf.cdo.util;
+
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class DanglingIntegrityException extends DataIntegrityException
+{
+  private static final long serialVersionUID = 1L;
+
+  public DanglingIntegrityException(DanglingReferenceException cause)
+  {
+    super(cause.getMessage(), cause);
+  }
+
+  @Override
+  public DanglingReferenceException getCause()
+  {
+    return (DanglingReferenceException)super.getCause();
+  }
+
+  public EObject getTarget()
+  {
+    return getCause().getTarget();
+  }
+
+  @Override
+  public boolean isLocal()
+  {
+    return true;
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingReferenceException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingReferenceException.java
index e5c5db0..e44104a 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingReferenceException.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DanglingReferenceException.java
@@ -4,7 +4,7 @@
  * 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
  *    Eike Stepper - maintenance
@@ -26,7 +26,7 @@
  * An unchecked exception being thrown from {@link CDOTransaction#commit()} if the commit {@link CDOCommitData change
  * set} is referencing {@link EObject objects} that are not contained by any {@link Resource resource} before the server
  * is contacted.
- * 
+ *
  * @author Simon McDuff
  * @since 2.0
  * @noextend This interface is not intended to be extended by clients.
@@ -36,7 +36,7 @@
 {
   private static final long serialVersionUID = 1L;
 
-  private EObject target;
+  private transient EObject target;
 
   public DanglingReferenceException(EObject object)
   {
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DataIntegrityException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DataIntegrityException.java
new file mode 100644
index 0000000..4977b32
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/DataIntegrityException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010-2012 Eike Stepper (Berlin, 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:
+ *    Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class DataIntegrityException extends CommitException
+{
+  private static final long serialVersionUID = 1L;
+
+  public DataIntegrityException()
+  {
+  }
+
+  public DataIntegrityException(String message, Throwable cause)
+  {
+    super(message, cause);
+  }
+
+  public DataIntegrityException(String message)
+  {
+    super(message);
+  }
+
+  public DataIntegrityException(Throwable cause)
+  {
+    super(cause);
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ImplicitLockingException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ImplicitLockingException.java
new file mode 100644
index 0000000..541b5e7
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ImplicitLockingException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2004-2013 Eike Stepper (Berlin, 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class ImplicitLockingException extends ConcurrentAccessException
+{
+  private static final long serialVersionUID = 1L;
+
+  public ImplicitLockingException(String message)
+  {
+    super(message);
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/LocalCommitConflictException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/LocalCommitConflictException.java
new file mode 100644
index 0000000..5034a36
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/LocalCommitConflictException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2004-2013 Eike Stepper (Berlin, 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:
+ *    Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.util;
+
+/**
+ * @author Eike Stepper
+ * @since 4.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class LocalCommitConflictException extends CommitConflictException
+{
+  private static final long serialVersionUID = 1L;
+
+  public LocalCommitConflictException(String message)
+  {
+    super(message);
+  }
+
+  @Override
+  public boolean isLocal()
+  {
+    return true;
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ReferentialIntegrityException.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ReferentialIntegrityException.java
index 01cd28b..b8ae2e2 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ReferentialIntegrityException.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/util/ReferentialIntegrityException.java
@@ -16,17 +16,17 @@
 
 /**
  * A {@link CommitException commit exception} that indicates referential integrity problems detected by the server.
- * 
+ *
  * @author Eike Stepper
  * @since 4.0
  * @noextend This interface is not intended to be extended by clients.
  * @noinstantiate This class is not intended to be instantiated by clients.
  */
-public class ReferentialIntegrityException extends CommitException
+public class ReferentialIntegrityException extends DataIntegrityException
 {
   private static final long serialVersionUID = 1L;
 
-  private List<CDOObjectReference> xRefs;
+  private transient List<CDOObjectReference> xRefs;
 
   public ReferentialIntegrityException(String msg, List<CDOObjectReference> xRefs)
   {
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
index 607d247..be434cc 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java
@@ -10,12 +10,15 @@
  */
 package org.eclipse.emf.internal.cdo.transaction;
 
-import org.eclipse.emf.cdo.CDOObjectReference;
 import org.eclipse.emf.cdo.common.branch.CDOBranch;
 import org.eclipse.emf.cdo.common.commit.CDOCommitData;
 import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
+import org.eclipse.emf.cdo.util.CommitConflictException;
 import org.eclipse.emf.cdo.util.CommitException;
+import org.eclipse.emf.cdo.util.ContainmentCycleException;
+import org.eclipse.emf.cdo.util.ImplicitLockingException;
 import org.eclipse.emf.cdo.util.ReferentialIntegrityException;
 
 import org.eclipse.emf.internal.cdo.bundle.OM;
@@ -35,8 +38,6 @@
 
 import org.eclipse.core.runtime.IProgressMonitor;
 
-import java.util.List;
-
 /**
  * @author Simon McDuff
  * @since 2.0
@@ -75,13 +76,27 @@
     String rollbackMessage = result.getRollbackMessage();
     if (rollbackMessage != null)
     {
-      List<CDOObjectReference> xRefs = result.getXRefs();
-      if (xRefs != null)
+      byte rollbackReason = result.getRollbackReason();
+      switch (rollbackReason)
       {
-        throw new ReferentialIntegrityException(rollbackMessage, xRefs);
-      }
+      case CDOProtocolConstants.ROLLBACK_REASON_IMPLICIT_LOCKING:
+        throw new ImplicitLockingException(rollbackMessage);
 
-      throw new CommitException(rollbackMessage);
+      case CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT:
+        throw new CommitConflictException(rollbackMessage);
+
+      case CDOProtocolConstants.ROLLBACK_REASON_CONTAINMENT_CYCLE:
+        throw new ContainmentCycleException(rollbackMessage);
+
+      case CDOProtocolConstants.ROLLBACK_REASON_REFERENTIAL_INTEGRITY:
+        throw new ReferentialIntegrityException(rollbackMessage, result.getXRefs());
+
+      case CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN:
+        throw new CommitException(rollbackMessage);
+
+      default:
+        throw new IllegalStateException("Invalid rollbackreason: " + rollbackReason);
+      }
     }
 
     transaction.setCommitComment(null);
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 a08f36c..da19276 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
@@ -100,6 +100,9 @@
 import org.eclipse.emf.cdo.util.CDOURIUtil;
 import org.eclipse.emf.cdo.util.CDOUtil;
 import org.eclipse.emf.cdo.util.CommitException;
+import org.eclipse.emf.cdo.util.DanglingIntegrityException;
+import org.eclipse.emf.cdo.util.DanglingReferenceException;
+import org.eclipse.emf.cdo.util.LocalCommitConflictException;
 import org.eclipse.emf.cdo.util.ObjectNotFoundException;
 
 import org.eclipse.emf.internal.cdo.CDOObjectImpl;
@@ -1176,7 +1179,7 @@
       checkActive();
       if (hasConflict())
       {
-        throw new CommitException(Messages.getString("CDOTransactionImpl.2")); //$NON-NLS-1$
+        throw new LocalCommitConflictException(Messages.getString("CDOTransactionImpl.2")); //$NON-NLS-1$
       }
 
       if (progressMonitor == null)
@@ -1193,6 +1196,10 @@
 
       return info;
     }
+    catch (DanglingReferenceException ex)
+    {
+      throw new DanglingIntegrityException(ex);
+    }
     catch (CommitException ex)
     {
       throw ex;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
index 222be34..9d4c2e5 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java
@@ -29,6 +29,7 @@
 import org.eclipse.emf.cdo.common.lock.CDOLockState;
 import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
 import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
 import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
@@ -675,6 +676,8 @@
   {
     private CDOIDProvider idProvider;
 
+    private byte rollbackReason;
+
     private String rollbackMessage;
 
     private List<CDOObjectReference> xRefs;
@@ -699,16 +702,19 @@
     public CommitTransactionResult(CDOIDProvider idProvider, String rollbackMessage, CDOBranchPoint branchPoint,
         long previousTimeStamp, List<CDOObjectReference> xRefs)
     {
-      this(idProvider, rollbackMessage, branchPoint, previousTimeStamp, xRefs, true);
+      this(idProvider, CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, rollbackMessage, branchPoint, previousTimeStamp,
+          xRefs, true);
     }
 
     /**
      * @since 4.2
      */
-    public CommitTransactionResult(CDOIDProvider idProvider, String rollbackMessage, CDOBranchPoint branchPoint,
-        long previousTimeStamp, List<CDOObjectReference> xRefs, boolean clearResourcePathCache)
+    public CommitTransactionResult(CDOIDProvider idProvider, byte rollbackReason, String rollbackMessage,
+        CDOBranchPoint branchPoint, long previousTimeStamp, List<CDOObjectReference> xRefs,
+        boolean clearResourcePathCache)
     {
       this.idProvider = idProvider;
+      this.rollbackReason = rollbackReason;
       this.rollbackMessage = rollbackMessage;
       this.branchPoint = branchPoint;
       this.previousTimeStamp = previousTimeStamp;
@@ -759,6 +765,14 @@
       this.referenceAdjuster = referenceAdjuster;
     }
 
+    /**
+     * @since 4.2
+     */
+    public byte getRollbackReason()
+    {
+      return rollbackReason;
+    }
+
     public String getRollbackMessage()
     {
       return rollbackMessage;