Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocol.java158
-rw-r--r--plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java12
-rw-r--r--plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java17
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java14
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java2
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java187
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ICommitConflictResolver.java157
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java5
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_547640_Test.java225
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java14
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOTransactionContainerImpl.java34
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOSingleTransactionStrategyImpl.java5
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java173
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java6
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java2
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java18
-rw-r--r--plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java2
-rw-r--r--plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java2
20 files changed, 928 insertions, 117 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocol.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocol.java
index d02a783b43..e15277ba59 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocol.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocol.java
@@ -15,8 +15,11 @@ import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
import org.eclipse.emf.cdo.common.security.CDOPermission;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import java.io.IOException;
import java.util.Map;
@@ -69,6 +72,8 @@ public interface CDOProtocol extends CDOProtocolConstants
private boolean clearResourcePathCache;
+ private boolean modifiedByServer;
+
private CDOLockChangeInfo lockChangeInfo;
public CommitNotificationInfo()
@@ -80,6 +85,7 @@ public interface CDOProtocol extends CDOProtocolConstants
senderID = in.readXInt();
commitInfo = in.readCDOCommitInfo();
clearResourcePathCache = in.readBoolean();
+ modifiedByServer = in.readBoolean();
securityImpact = in.readByte();
int size = in.readXInt();
@@ -107,6 +113,7 @@ public interface CDOProtocol extends CDOProtocolConstants
out.writeXInt(senderID);
out.writeCDOCommitInfo(commitInfo);
out.writeBoolean(clearResourcePathCache);
+ out.writeBoolean(modifiedByServer);
out.writeByte(securityImpact); // Must come after writeCDOCommitInfo()
// Must come after writeCDOCommitInfo()
@@ -217,6 +224,22 @@ public interface CDOProtocol extends CDOProtocolConstants
}
/**
+ * @since 4.8
+ */
+ public boolean isModifiedByServer()
+ {
+ return modifiedByServer;
+ }
+
+ /**
+ * @since 4.8
+ */
+ public void setModifiedByServer(boolean modifiedByServer)
+ {
+ this.modifiedByServer = modifiedByServer;
+ }
+
+ /**
* @since 4.6
*/
public CDOLockChangeInfo getLockChangeInfo()
@@ -232,4 +255,139 @@ public interface CDOProtocol extends CDOProtocolConstants
this.lockChangeInfo = lockChangeInfo;
}
}
+
+ /**
+ * @author Eike Stepper
+ * @since 4.8
+ */
+ public static final class CommitData
+ {
+ private final InternalCDORevision[] newObjects;
+
+ private final InternalCDORevisionDelta[] dirtyObjectDeltas;
+
+ private final CDOID[] detachedObjects;
+
+ public CommitData(InternalCDORevision[] newObjects, InternalCDORevisionDelta[] dirtyObjectDeltas, CDOID[] detachedObjects)
+ {
+ this.newObjects = newObjects;
+ this.dirtyObjectDeltas = dirtyObjectDeltas;
+ this.detachedObjects = detachedObjects;
+ }
+
+ public CommitData(CDODataInput in) throws IOException
+ {
+ newObjects = readNewObjects(in);
+ dirtyObjectDeltas = readDirtyObjectDeltas(in);
+ detachedObjects = readDetachedObjects(in);
+ }
+
+ private InternalCDORevision[] readNewObjects(CDODataInput in) throws IOException
+ {
+ InternalCDORevision[] result = null;
+
+ int n = in.readXInt();
+ if (n > 0)
+ {
+ result = new InternalCDORevision[n];
+ for (int i = 0; i < n; i++)
+ {
+ result[i] = (InternalCDORevision)in.readCDORevision();
+ }
+ }
+
+ return result;
+ }
+
+ private InternalCDORevisionDelta[] readDirtyObjectDeltas(CDODataInput in) throws IOException
+ {
+ InternalCDORevisionDelta[] result = null;
+
+ int n = in.readXInt();
+ if (n > 0)
+ {
+ result = new InternalCDORevisionDelta[n];
+ for (int i = 0; i < n; i++)
+ {
+ result[i] = (InternalCDORevisionDelta)in.readCDORevisionDelta();
+ }
+ }
+
+ return result;
+ }
+
+ private CDOID[] readDetachedObjects(CDODataInput in) throws IOException
+ {
+ CDOID[] result = null;
+
+ int n = in.readXInt();
+ if (n > 0)
+ {
+ result = new CDOID[n];
+ for (int i = 0; i < n; i++)
+ {
+ result[i] = in.readCDOID();
+ }
+ }
+
+ return result;
+ }
+
+ public void write(CDODataOutput out) throws IOException
+ {
+ if (newObjects != null)
+ {
+ out.writeXInt(newObjects.length);
+ for (int i = 0; i < newObjects.length; i++)
+ {
+ out.writeCDORevision(newObjects[i], CDORevision.UNCHUNKED);
+ }
+ }
+ else
+ {
+ out.writeXInt(0);
+ }
+
+ if (dirtyObjectDeltas != null)
+ {
+ out.writeXInt(dirtyObjectDeltas.length);
+ for (int i = 0; i < dirtyObjectDeltas.length; i++)
+ {
+ out.writeCDORevisionDelta(dirtyObjectDeltas[i]);
+ }
+ }
+ else
+ {
+ out.writeXInt(0);
+ }
+
+ if (detachedObjects != null)
+ {
+ out.writeXInt(detachedObjects.length);
+ for (int i = 0; i < detachedObjects.length; i++)
+ {
+ out.writeCDOID(detachedObjects[i]);
+ }
+ }
+ else
+ {
+ out.writeXInt(0);
+ }
+ }
+
+ public InternalCDORevision[] getNewObjects()
+ {
+ return newObjects;
+ }
+
+ public InternalCDORevisionDelta[] getDirtyObjectDeltas()
+ {
+ return dirtyObjectDeltas;
+ }
+
+ public CDOID[] getDetachedObjects()
+ {
+ return detachedObjects;
+ }
+ }
}
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 4044b509ec..514256b551 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
@@ -31,6 +31,8 @@ import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDORevision;
@@ -344,6 +346,7 @@ public class CommitTransactionRequest extends CDOClientRequestWithMonitoring<Com
confirmingMappingNewObjects(in, result);
confirmingNewLockStates(in, result);
confirmingNewPermissions(in, result);
+ confirmingNewCommitData(in, result);
return result;
}
@@ -440,4 +443,13 @@ public class CommitTransactionRequest extends CDOClientRequestWithMonitoring<Com
}
}
}
+
+ protected void confirmingNewCommitData(CDODataInput in, CommitTransactionResult result) throws IOException
+ {
+ if (in.readBoolean())
+ {
+ CommitData commitData = new CDOProtocol.CommitData(in);
+ result.setNewCommitData(commitData);
+ }
+ }
}
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 7debf2eb25..f6789ba209 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
@@ -25,6 +25,7 @@ import org.eclipse.emf.cdo.common.model.EMFUtil;
import org.eclipse.emf.cdo.common.model.EMFUtil.ExtResourceSet;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.security.CDOPermission;
@@ -344,6 +345,7 @@ public class CommitTransactionIndication extends CDOServerIndicationWithMonitori
respondingMappingNewObjects(out);
respondingNewLockStates(out);
respondingNewPermissions(out);
+ respondingNewCommitData(out);
}
}
finally
@@ -435,7 +437,6 @@ public class CommitTransactionIndication extends CDOServerIndicationWithMonitori
out.writeXInt(newObjects.length + dirtyObjects.length);
respondingNewPermissions(out, permissionManager, session, newObjects);
respondingNewPermissions(out, permissionManager, session, dirtyObjects);
-
return;
}
}
@@ -461,6 +462,20 @@ public class CommitTransactionIndication extends CDOServerIndicationWithMonitori
}
}
+ protected void respondingNewCommitData(CDODataOutput out) throws Exception
+ {
+ CommitData originalCommmitData = commitContext.getOriginalCommmitData();
+ if (originalCommmitData != null)
+ {
+ out.writeBoolean(true);
+ new CommitData(commitContext.getNewObjects(), commitContext.getDirtyObjectDeltas(), commitContext.getDetachedObjects()).write(out);
+ }
+ else
+ {
+ out.writeBoolean(false);
+ }
+ }
+
protected InternalTransaction getTransaction(int viewID)
{
InternalView view = getView(viewID);
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
index 1b25c76446..7a2f2fc106 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java
@@ -84,6 +84,7 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo;
import org.eclipse.emf.cdo.spi.server.ContainerQueryHandlerProvider;
+import org.eclipse.emf.cdo.spi.server.ICommitConflictResolver;
import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
import org.eclipse.emf.cdo.spi.server.InternalCommitManager;
import org.eclipse.emf.cdo.spi.server.InternalLockManager;
@@ -199,6 +200,8 @@ public class Repository extends Container<Object> implements InternalRepository,
private InternalCDOCommitInfoManager commitInfoManager;
+ private ICommitConflictResolver commitConflictResolver;
+
private InternalSessionManager sessionManager;
private InternalQueryManager queryManager;
@@ -1045,6 +1048,17 @@ public class Repository extends Container<Object> implements InternalRepository,
this.commitInfoManager = commitInfoManager;
}
+ public ICommitConflictResolver getCommitConflictResolver()
+ {
+ return commitConflictResolver;
+ }
+
+ public void setCommitConflictResolver(ICommitConflictResolver commitConflictResolver)
+ {
+ checkInactive();
+ this.commitConflictResolver = commitConflictResolver;
+ }
+
public InternalCDORevisionManager getRevisionManager()
{
return revisionManager;
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java
index d29755b0b2..d2bf461b4e 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java
@@ -340,7 +340,7 @@ public class SessionManager extends Container<ISession> implements InternalSessi
CDOCommonSession sender = info.getSender();
for (InternalSession session : getSessions())
{
- if (session != sender)
+ if (session != sender || info.isModifiedByServer())
{
if (session.isOpenOnClientSide())
{
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 9a929358e7..dc428afab3 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
@@ -17,6 +17,7 @@ import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
import org.eclipse.emf.cdo.common.commit.CDOCommitData;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.id.CDOID;
@@ -29,6 +30,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.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch;
@@ -67,6 +69,7 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.StubCDORevision;
+import org.eclipse.emf.cdo.spi.server.ICommitConflictResolver;
import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
import org.eclipse.emf.cdo.spi.server.InternalLockManager;
import org.eclipse.emf.cdo.spi.server.InternalRepository;
@@ -171,6 +174,8 @@ public class TransactionCommitContext implements InternalCommitContext
private Map<CDOID, InternalCDORevision> newRevisions;
+ private CommitData originalCommmitData;
+
private Set<Object> lockedObjects = new HashSet<Object>();
private List<CDOID> lockedTargets;
@@ -377,6 +382,11 @@ public class TransactionCommitContext implements InternalCommitContext
return newRevisions;
}
+ public CommitData getOriginalCommmitData()
+ {
+ return originalCommmitData;
+ }
+
public InternalCDORevision getRevision(CDOID id)
{
if (cachedRevisions == null)
@@ -705,7 +715,7 @@ public class TransactionCommitContext implements InternalCommitContext
{
try
{
- monitor.begin(107);
+ monitor.begin(106);
hasChanges = newPackageUnits.length != 0 || newObjects.length != 0 || dirtyObjectDeltas.length != 0;
if (!hasChanges)
@@ -922,6 +932,7 @@ public class TransactionCommitContext implements InternalCommitContext
protected void sendCommitNotifications(boolean success)
{
commitNotificationInfo.setSender(transaction.getSession());
+ commitNotificationInfo.setModifiedByServer(originalCommmitData != null);
commitNotificationInfo.setRevisionProvider(this);
commitNotificationInfo.setLockChangeInfo(lockChangeInfo);
@@ -1173,21 +1184,35 @@ public class TransactionCommitContext implements InternalCommitContext
try
{
monitor.begin(dirtyObjectDeltas.length);
+ List<InternalCDORevisionDelta> conflicts = new ArrayList<InternalCDORevisionDelta>();
+
for (int i = 0; i < dirtyObjectDeltas.length; i++)
{
- dirtyObjects[i] = computeDirtyObject(dirtyObjectDeltas[i]);
- if (dirtyObjects[i] == null)
+ InternalCDORevision newRevision = computeDirtyObject(dirtyObjectDeltas[i]);
+ if (newRevision == null)
{
- throw new IllegalStateException("Can not retrieve origin revision for " + dirtyObjectDeltas[i]); //$NON-NLS-1$
+ conflicts.add(dirtyObjectDeltas[i]);
}
-
- if (!dirtyObjects[i].isWritable())
+ else if (!newRevision.isWritable())
{
- throw new NoPermissionException(dirtyObjects[i]);
+ throw new NoPermissionException(newRevision);
}
+ dirtyObjects[i] = newRevision;
monitor.worked();
}
+
+ if (!conflicts.isEmpty())
+ {
+ dirtyObjects = new InternalCDORevision[dirtyObjectDeltas.length];
+ mergeConflicts(conflicts);
+ }
+
+ if (!conflicts.isEmpty())
+ {
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT,
+ "Attempt by " + transaction + " to modify historical revisions: " + conflicts);
+ }
}
finally
{
@@ -1197,55 +1222,149 @@ public class TransactionCommitContext implements InternalCommitContext
protected InternalCDORevision computeDirtyObject(InternalCDORevisionDelta delta)
{
- CDOID id = delta.getID();
-
- InternalCDORevision oldRevision = null;
- String rollbackMessage = null;
- byte rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN;
-
try
{
- oldRevision = (InternalCDORevision)transaction.getRevision(id);
- if (oldRevision != null)
+ CDOID id = delta.getID();
+
+ InternalCDORevision oldRevision = (InternalCDORevision)transaction.getRevision(id);
+ if (oldRevision == null)
{
- if (oldRevision.getBranch() != delta.getBranch() || oldRevision.getVersion() != delta.getVersion())
- {
- rollbackMessage = "Attempt by " + transaction + " to modify historical revision: " + delta;
- rollbackReason = CDOProtocolConstants.ROLLBACK_REASON_COMMIT_CONFLICT;
- }
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, "Revision " + id + " not found by " + transaction);
}
- else
+
+ repository.ensureChunks(oldRevision, CDORevision.UNCHUNKED);
+ oldRevisions.put(id, oldRevision);
+
+ if (oldRevision.getBranch() != delta.getBranch() || oldRevision.getVersion() != delta.getVersion())
{
- rollbackMessage = "Revision " + id + " not found by " + transaction;
+ // Commit conflict!
+ return null;
}
+
+ InternalCDORevision newRevision = oldRevision.copy();
+ newRevision.adjustForCommit(branch, timeStamp);
+
+ delta.applyTo(newRevision);
+ return newRevision;
+ }
+ catch (RollbackException ex)
+ {
+ throw ex;
}
catch (Exception ex)
{
OM.LOG.error(ex);
- rollbackMessage = ex.getMessage();
+ String rollbackMessage = ex.getMessage();
if (rollbackMessage == null)
{
rollbackMessage = ex.getClass().getName();
}
+
+ throw new RollbackException(CDOProtocolConstants.ROLLBACK_REASON_UNKNOWN, rollbackMessage);
}
+ }
- if (rollbackMessage != null)
+ /**
+ * When this method is called, the oldRevisions map is filled with the latest valid revisions, chunks ensured.
+ */
+ protected void mergeConflicts(List<InternalCDORevisionDelta> conflicts)
+ {
+ ICommitConflictResolver commitConflictResolver = repository.getCommitConflictResolver();
+ if (commitConflictResolver != null)
{
- // If the object is logically locked (see lockObjects) but has a wrong (newer) version, someone else modified it.
- throw new RollbackException(rollbackReason, rollbackMessage);
- }
+ CDOChangeSetData result = commitConflictResolver.resolveConflicts(this, conflicts);
+ if (result != null)
+ {
+ originalCommmitData = new CommitData(newObjects, dirtyObjectDeltas, detachedObjects);
+
+ // Apply new objects.
+ {
+ List<InternalCDORevision> newObjectsList = new ArrayList<InternalCDORevision>();
+
+ List<CDOIDAndVersion> idAndVersions = result.getNewObjects();
+ if (idAndVersions != null)
+ {
+ for (CDOIDAndVersion idAndVersion : idAndVersions)
+ {
+ if (idAndVersion instanceof InternalCDORevision)
+ {
+ InternalCDORevision newObject = (InternalCDORevision)idAndVersion;
+ if (newObject.getID().isTemporary())
+ {
+ newObjectsList.add(newObject);
+ }
+ }
+ }
+ }
- // Make sure all chunks are loaded
- repository.ensureChunks(oldRevision, CDORevision.UNCHUNKED);
+ newObjects = newObjectsList.toArray(new InternalCDORevision[newObjectsList.size()]);
+ }
- oldRevisions.put(id, oldRevision);
+ // Apply detached objects.
+ {
+ List<CDOID> detachedObjectsList = new ArrayList<CDOID>();
+ List<CDOBranchVersion> detachedObjectVersionsList = new ArrayList<CDOBranchVersion>();
+ detachedObjectTypes = new HashMap<CDOID, EClass>();
- InternalCDORevision newRevision = oldRevision.copy();
- newRevision.adjustForCommit(branch, timeStamp);
+ List<CDOIDAndVersion> idAndVersions = result.getDetachedObjects();
+ if (idAndVersions != null)
+ {
+ for (CDOIDAndVersion idAndVersion : idAndVersions)
+ {
+ CDOID id = idAndVersion.getID();
+
+ InternalCDORevision oldObject = (InternalCDORevision)transaction.getRevision(id);
+ if (oldObject != null)
+ {
+ detachedObjectsList.add(id);
+ detachedObjectVersionsList.add(oldObject.getBranch().getVersion(oldObject.getVersion()));
+ detachedObjectTypes.put(id, oldObject.getEClass());
+ }
+ }
+ }
- delta.applyTo(newRevision);
- return newRevision;
+ detachedObjects = detachedObjectsList.toArray(new CDOID[detachedObjectsList.size()]);
+ detachedObjectVersions = detachedObjectVersionsList.toArray(new CDOBranchVersion[detachedObjectVersionsList.size()]);
+ }
+
+ // Apply changed objects.
+ {
+ List<InternalCDORevisionDelta> dirtyObjectDeltasList = new ArrayList<InternalCDORevisionDelta>();
+ List<InternalCDORevision> dirtyObjectsList = new ArrayList<InternalCDORevision>();
+ InternalCDORevisionManager revisionManager = repository.getRevisionManager();
+
+ List<CDORevisionKey> revisionKeys = result.getChangedObjects();
+ if (revisionKeys != null)
+ {
+ for (CDORevisionKey revisionKey : revisionKeys)
+ {
+ if (revisionKey instanceof InternalCDORevisionDelta)
+ {
+ InternalCDORevisionDelta ancestorDelta = (InternalCDORevisionDelta)revisionKey;
+ CDOID id = ancestorDelta.getID();
+
+ InternalCDORevision ancestorRevision = revisionManager.getRevisionByVersion(id, ancestorDelta, CDORevision.UNCHUNKED, true);
+ InternalCDORevision newRevision = ancestorRevision.copy();
+ ancestorDelta.applyTo(newRevision);
+
+ InternalCDORevision oldRevision = (InternalCDORevision)transaction.getRevision(id);
+
+ newRevision.setVersion(oldRevision.getVersion()); // Needed in adjustForCommit().
+ newRevision.adjustForCommit(branch, timeStamp);
+
+ InternalCDORevisionDelta dirtyObjectDelta = newRevision.compare(oldRevision);
+ dirtyObjectDeltasList.add(dirtyObjectDelta);
+ dirtyObjectsList.add(newRevision);
+ }
+ }
+ }
+
+ dirtyObjectDeltas = dirtyObjectDeltasList.toArray(new InternalCDORevisionDelta[dirtyObjectDeltasList.size()]);
+ dirtyObjects = dirtyObjectsList.toArray(new InternalCDORevision[dirtyObjectsList.size()]);
+ }
+ }
+ }
}
protected void checkContainmentCycles()
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java
index 6869fbe86b..d96706dbdc 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java
@@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
+import org.eclipse.emf.cdo.spi.server.ICommitConflictResolver;
import org.eclipse.net4j.util.container.IContainer;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
@@ -83,6 +84,11 @@ public interface IRepository extends CDOCommonRepository, IQueryHandlerProvider,
*/
public CDOCommitInfoManager getCommitInfoManager();
+ /**
+ * @since 4.8
+ */
+ public ICommitConflictResolver getCommitConflictResolver();
+
public ISessionManager getSessionManager();
/**
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 e0b8bf7a7e..dadff4a262 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
@@ -25,6 +25,7 @@ import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
@@ -395,6 +396,11 @@ public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, Com
public Map<CDOID, InternalCDORevision> getNewRevisions();
/**
+ * @since 4.8
+ */
+ public CommitData getOriginalCommmitData();
+
+ /**
* Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the
* stream is:
* <p>
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ICommitConflictResolver.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ICommitConflictResolver.java
new file mode 100644
index 0000000000..f50f7a3f28
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ICommitConflictResolver.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2004-2018 Eike Stepper (Loehne, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.spi.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSet;
+import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
+import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
+import org.eclipse.emf.cdo.server.IStoreAccessor;
+import org.eclipse.emf.cdo.server.IStoreAccessor.CommitContext;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
+import org.eclipse.emf.cdo.transaction.CDOMerger;
+import org.eclipse.emf.cdo.transaction.CDOMerger.ConflictException;
+
+import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
+
+import java.util.AbstractList;
+import java.util.List;
+
+/**
+ * @author Eike Stepper
+ * @since 4.8
+ */
+public interface ICommitConflictResolver
+{
+ /**
+ * When this method is called, the {@link CommitContext#getOldRevisions()} map is filled with the latest valid revisions, chunks ensured.
+ */
+ public CDOChangeSetData resolveConflicts(IStoreAccessor.CommitContext commitContext, List<InternalCDORevisionDelta> conflicts);
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Merging implements ICommitConflictResolver
+ {
+ public CDOChangeSetData resolveConflicts(IStoreAccessor.CommitContext commitContext, List<InternalCDORevisionDelta> conflicts)
+ {
+ CDOBranchPoint sourceEndPoint = commitContext.getBranchPoint();
+ CDOBranch branch = sourceEndPoint.getBranch();
+ CDOBranchPoint startPoint = branch.getPoint(commitContext.getLastUpdateTime());
+ CDOBranchPoint targetEndPoint = branch.getPoint(commitContext.getTransaction().getRepository().getLastCommitTimeStamp());
+
+ CDOChangeSet source = createSourceChangeSet(commitContext, startPoint, sourceEndPoint);
+ CDOChangeSet target = createTargetChangeSet(commitContext, startPoint, targetEndPoint);
+ CDOMerger merger = createMerger();
+
+ try
+ {
+ CDOChangeSetData result = merger.merge(target, source);
+ conflicts.clear();
+ return result;
+ }
+ catch (ConflictException ex)
+ {
+ CDOChangeSetData result = ex.getResult();
+ for (CDORevisionKey revisionKey : result.getChangedObjects())
+ {
+ conflicts.remove(revisionKey);
+ }
+ }
+
+ return null;
+ }
+
+ protected CDOMerger createMerger()
+ {
+ return new DefaultCDOMerger.PerFeature.ManyValued();
+ }
+
+ private CDOChangeSet createSourceChangeSet(final IStoreAccessor.CommitContext commitContext, CDOBranchPoint startPoint, CDOBranchPoint endPoint)
+ {
+ List<CDOIDAndVersion> newObjects = new AbstractList<CDOIDAndVersion>()
+ {
+ private final InternalCDORevision[] revisions = commitContext.getNewObjects();
+
+ private final int size = revisions.length;
+
+ @Override
+ public int size()
+ {
+ return size;
+ }
+
+ @Override
+ public CDOIDAndVersion get(int index)
+ {
+ return revisions[index];
+ }
+ };
+
+ List<CDORevisionKey> changedObjects = new AbstractList<CDORevisionKey>()
+ {
+ private final InternalCDORevisionDelta[] revisionDeltas = commitContext.getDirtyObjectDeltas();
+
+ private final int size = revisionDeltas.length;
+
+ @Override
+ public int size()
+ {
+ return size;
+ }
+
+ @Override
+ public CDORevisionKey get(int index)
+ {
+ return revisionDeltas[index];
+ }
+ };
+
+ List<CDOIDAndVersion> detachedObjects = new AbstractList<CDOIDAndVersion>()
+ {
+ private final CDOID[] ids = commitContext.getDetachedObjects();
+
+ private final CDOBranchVersion[] branchVersions = commitContext.getDetachedObjectVersions();
+
+ private final int size = ids.length;
+
+ @Override
+ public int size()
+ {
+ return size;
+ }
+
+ @Override
+ public CDOIDAndVersion get(int index)
+ {
+ return CDOIDUtil.createIDAndVersion(ids[index], branchVersions[index].getVersion());
+ }
+ };
+
+ CDOChangeSetData sourceChangeSetData = CDORevisionUtil.createChangeSetData(newObjects, changedObjects, detachedObjects);
+ return CDORevisionUtil.createChangeSet(startPoint, endPoint, sourceChangeSetData);
+ }
+
+ private CDOChangeSet createTargetChangeSet(IStoreAccessor.CommitContext commitContext, CDOBranchPoint startPoint, CDOBranchPoint endPoint)
+ {
+ InternalRepository repository = (InternalRepository)commitContext.getTransaction().getRepository();
+ CDOChangeSetData targetChangeSetData = repository.getChangeSet(startPoint, endPoint);
+ return CDORevisionUtil.createChangeSet(startPoint, endPoint, targetChangeSetData);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
index 2e3d168e1c..07ebcf85d9 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java
@@ -114,6 +114,11 @@ public interface InternalRepository extends IRepository, PackageProcessor, Packa
public InternalCDOCommitInfoManager getCommitInfoManager();
+ /**
+ * @since 4.8
+ */
+ public void setCommitConflictResolver(ICommitConflictResolver commitConflictResolver);
+
public InternalSessionManager getSessionManager();
public void setSessionManager(InternalSessionManager sessionManager);
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_547640_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_547640_Test.java
new file mode 100644
index 0000000000..494f2b9f64
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_547640_Test.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018 Eike Stepper (Loehne, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.emf.cdo.tests.bugzilla;
+
+import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode;
+import org.eclipse.emf.cdo.common.branch.CDOBranch;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.internal.server.SessionManager;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.spi.server.ICommitConflictResolver;
+import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.config.IRepositoryConfig;
+import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter;
+import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore;
+import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig;
+import org.eclipse.emf.cdo.tests.model1.Company;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOUtil;
+
+import org.eclipse.emf.internal.cdo.session.CDOTransactionContainerImpl.TransactionCreator;
+import org.eclipse.emf.internal.cdo.transaction.CDOTransactionImpl;
+
+import org.eclipse.net4j.util.io.IOUtil;
+
+import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Bug 547640 - Support server-side commit conflict resolution.
+ *
+ * @author Eike Stepper
+ */
+@CleanRepositoriesBefore(reason = "server-side merging")
+@CleanRepositoriesAfter(reason = "server-side merging")
+public class Bugzilla_547640_Test extends AbstractCDOTest
+{
+ @Requires(IRepositoryConfig.CAPABILITY_AUDITING)
+ public void testCommitConflictResolver_OneCommit() throws Exception
+ {
+ run(new TestLogic()
+ {
+ private int expectedVersion;
+
+ public void modifyAndCommit1(CDOTransaction transaction, Company company) throws Exception
+ {
+ company.getCategories().add(getModel1Factory().createCategory());
+ transaction.commit();
+ expectedVersion = CDOUtil.getCDOObject(company).cdoRevision().getVersion() + 1;
+ }
+
+ public void modify2(CDOTransaction transaction, Company company) throws Exception
+ {
+ company.getCategories().add(getModel1Factory().createCategory());
+ }
+
+ public void verify2(CDOTransaction transaction, Company company) throws Exception
+ {
+ CDORevision cdoRevision = CDOUtil.getCDOObject(company).cdoRevision();
+ assertEquals(expectedVersion, cdoRevision.getVersion());
+
+ assertEquals(2, company.getCategories().size());
+ }
+ });
+ }
+
+ @Requires(IRepositoryConfig.CAPABILITY_AUDITING)
+ public void testCommitConflictResolver_TwoCommits() throws Exception
+ {
+ run(new TestLogic()
+ {
+ private int expectedVersion;
+
+ public void modifyAndCommit1(CDOTransaction transaction, Company company) throws Exception
+ {
+ company.getCategories().add(getModel1Factory().createCategory());
+ transaction.commit();
+ company.getCategories().add(getModel1Factory().createCategory());
+ transaction.commit();
+ expectedVersion = CDOUtil.getCDOObject(company).cdoRevision().getVersion() + 1;
+ }
+
+ public void modify2(CDOTransaction transaction, Company company) throws Exception
+ {
+ company.getCategories().add(getModel1Factory().createCategory());
+ }
+
+ public void verify2(CDOTransaction transaction, Company company) throws Exception
+ {
+ CDORevision cdoRevision = CDOUtil.getCDOObject(company).cdoRevision();
+ assertEquals(expectedVersion, cdoRevision.getVersion());
+ }
+ });
+ }
+
+ @Override
+ protected void doSetUp() throws Exception
+ {
+ super.doSetUp();
+ getTestProperties().put(RepositoryConfig.PROP_TEST_COMMIT_CONFLICT_RESOLVER, new ICommitConflictResolver.Merging());
+ getTestProperties().put(RepositoryConfig.PROP_TEST_SESSION_MANAGER, new SuspendableSessionManager());
+ }
+
+ @Override
+ protected void doTearDown() throws Exception
+ {
+ TransactionCreator.reset();
+ super.doTearDown();
+ }
+
+ protected SuspendableSessionManager getSessionManager()
+ {
+ return (SuspendableSessionManager)getTestProperties().get(RepositoryConfig.PROP_TEST_SESSION_MANAGER);
+ }
+
+ private void run(final TestLogic testLogic) throws Exception
+ {
+ CDOSession session1 = openSession();
+ CDOTransaction transaction1 = session1.openTransaction();
+ Company company1 = getModel1Factory().createCompany();
+ transaction1.createResource(getResourcePath("resource")).getContents().add(company1);
+ transaction1.commit();
+
+ CDOSession session2 = openSession();
+ session2.options().setPassiveUpdateMode(PassiveUpdateMode.CHANGES);
+ TransactionCreator.set(new TransactionCreator()
+ {
+ @Override
+ public InternalCDOTransaction createTransaction(CDOSession session, CDOBranch branch)
+ {
+ return new CDOTransactionImpl(session, branch)
+ {
+ @Override
+ protected void waitForBaseline(long previousTimeStamp)
+ {
+ getSessionManager().resume();
+ super.waitForBaseline(previousTimeStamp);
+ }
+ };
+ }
+ });
+
+ final CDOTransaction transaction2 = session2.openTransaction();
+ TransactionCreator.reset();
+
+ final Company company2 = (Company)transaction2.getResource(getResourcePath("resource")).getContents().get(0);
+
+ // Let client2 modify the model (but not commit, yet).
+ testLogic.modify2(transaction2, company2);
+
+ // Let client1 modify the model and commit (but hold back commit notifications to client2).
+ getSessionManager().suspend();
+ testLogic.modifyAndCommit1(transaction1, company1);
+
+ // Let client2 commit and verify the result.
+ transaction2.commit();
+ testLogic.verify2(transaction2, company2);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static class SuspendableSessionManager extends SessionManager
+ {
+ private List<CommitNotificationInfo> queue;
+
+ public synchronized void suspend()
+ {
+ IOUtil.OUT().println("Suspending commit notifications");
+ queue = new ArrayList<CommitNotificationInfo>();
+ }
+
+ public synchronized void resume()
+ {
+ IOUtil.OUT().println("Resuming commit notifications");
+
+ try
+ {
+ for (CommitNotificationInfo info : queue)
+ {
+ super.sendCommitNotification(info);
+ }
+ }
+ finally
+ {
+ queue = null;
+ }
+ }
+
+ @Override
+ public synchronized void sendCommitNotification(CommitNotificationInfo info)
+ {
+ if (queue != null)
+ {
+ queue.add(info);
+ }
+ else
+ {
+ super.sendCommitNotification(info);
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public interface TestLogic
+ {
+ public void modifyAndCommit1(CDOTransaction transaction, Company company) throws Exception;
+
+ public void modify2(CDOTransaction transaction, Company company) throws Exception;
+
+ public void verify2(CDOTransaction transaction, Company company) throws Exception;
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
index 98ce494644..4348251407 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java
@@ -52,6 +52,7 @@ import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
+import org.eclipse.emf.cdo.spi.server.ICommitConflictResolver;
import org.eclipse.emf.cdo.spi.server.InternalRepository;
import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer;
import org.eclipse.emf.cdo.spi.server.InternalSessionManager;
@@ -131,6 +132,8 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf
public static final String PROP_TEST_QUERY_HANDLER_PROVIDER = "test.repository.QueryHandlerProvider";
+ public static final String PROP_TEST_COMMIT_CONFLICT_RESOLVER = "test.repository.CommitConflictResolver";
+
public static final String PROP_TEST_ENABLE_SERVER_BROWSER = "test.repository.EnableServerBrowser";
private static final boolean LOG_MULTI_VIEW_COMMIT = false;
@@ -776,6 +779,12 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf
repository.setQueryHandlerProvider(queryHandlerProvider);
}
+ ICommitConflictResolver commitConflictResolver = getTestCommitConflictResolver();
+ if (commitConflictResolver != null)
+ {
+ repository.setCommitConflictResolver(commitConflictResolver);
+ }
+
return repository;
}
@@ -824,6 +833,11 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf
return (IQueryHandlerProvider)getTestProperty(PROP_TEST_QUERY_HANDLER_PROVIDER);
}
+ protected ICommitConflictResolver getTestCommitConflictResolver()
+ {
+ return (ICommitConflictResolver)getTestProperty(PROP_TEST_COMMIT_CONFLICT_RESOLVER);
+ }
+
protected boolean needsCleanRepos()
{
IScenario scenario = getCurrentTest().getScenario();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOTransactionContainerImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOTransactionContainerImpl.java
index d79fe7b604..4e79f2f559 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOTransactionContainerImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOTransactionContainerImpl.java
@@ -116,7 +116,7 @@ public abstract class CDOTransactionContainerImpl extends CDOViewContainerImpl i
*/
protected InternalCDOTransaction createTransaction(CDOBranch branch)
{
- return new CDOTransactionImpl((CDOSession)this, branch);
+ return TransactionCreator.instance.createTransaction((CDOSession)this, branch);
}
/**
@@ -124,6 +124,36 @@ public abstract class CDOTransactionContainerImpl extends CDOViewContainerImpl i
*/
protected InternalCDOTransaction createTransaction(String durableLockingID)
{
- return new CDOTransactionImpl((CDOSession)this, durableLockingID);
+ return TransactionCreator.instance.createTransaction((CDOSession)this, durableLockingID);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class TransactionCreator
+ {
+ public static final TransactionCreator DEFAULT = new TransactionCreator();
+
+ private static TransactionCreator instance = DEFAULT;
+
+ public static void set(TransactionCreator creator)
+ {
+ instance = creator;
+ }
+
+ public static void reset()
+ {
+ instance = DEFAULT;
+ }
+
+ public InternalCDOTransaction createTransaction(CDOSession session, CDOBranch branch)
+ {
+ return new CDOTransactionImpl(session, branch);
+ }
+
+ public InternalCDOTransaction createTransaction(CDOSession session, String durableLockingID)
+ {
+ return new CDOTransactionImpl(session, durableLockingID);
+ }
}
}
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 a5391d1506..a717ce7c67 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
@@ -64,6 +64,11 @@ public class CDOSingleTransactionStrategyImpl implements CDOTransactionStrategy
commitContext.postCommit(result);
+ if (result.getNewCommitData() != null)
+ {
+ return null;
+ }
+
String rollbackMessage = result.getRollbackMessage();
if (rollbackMessage != 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 e8017cc1e3..19b7daf21e 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
@@ -48,6 +48,7 @@ import org.eclipse.emf.cdo.common.model.EMFUtil;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDOList;
import org.eclipse.emf.cdo.common.revision.CDOListFactory;
@@ -1588,18 +1589,29 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
CDOCommitInfo info = commitSynced(monitor);
if (info != null)
{
- long timeStamp = info.getTimeStamp();
- long timeout = options().getCommitInfoTimeout();
-
- if (!waitForUpdate(timeStamp, timeout))
- {
- throw new TimeoutRuntimeException("Did not receive an update: " + this);
- }
+ waitForCommitInfo(info.getTimeStamp());
}
return info;
}
+ private void waitForCommitInfo(long timeStamp)
+ {
+ long timeout = options().getCommitInfoTimeout();
+ if (!waitForUpdate(timeStamp, timeout))
+ {
+ throw new TimeoutRuntimeException("Did not receive an update: " + this);
+ }
+ }
+
+ /**
+ * Overridden by Bugzilla_547640_Test to resume the repository's commit notification sending.
+ */
+ protected void waitForBaseline(long timeStamp)
+ {
+ waitForCommitInfo(timeStamp);
+ }
+
private CDOCommitInfo commitSynced(IProgressMonitor progressMonitor) throws DanglingIntegrityException, CommitException
{
synchronized (getViewMonitor())
@@ -4610,12 +4622,13 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
{
InternalCDOSession session = getSession();
long timeStamp = result.getTimeStamp();
+ long previousTimeStamp = result.getPreviousTimeStamp();
boolean clearResourcePathCache = result.isClearResourcePathCache();
if (result.getRollbackMessage() != null)
{
InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
- CDOCommitInfo commitInfo = new FailureCommitInfo(commitInfoManager, timeStamp, result.getPreviousTimeStamp());
+ CDOCommitInfo commitInfo = new FailureCommitInfo(commitInfoManager, timeStamp, previousTimeStamp);
InvalidationData invalidationData = new InvalidationData();
invalidationData.setCommitInfo(commitInfo);
@@ -4642,95 +4655,103 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
((InternalCDOPackageUnit)newPackageUnit).setState(CDOPackageUnit.State.LOADED);
}
- Map<CDOID, CDOObject> newObjects = getNewObjects();
- postCommit(newObjects, result);
-
- Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
- postCommit(dirtyObjects, result);
-
- for (CDORevisionDelta delta : getRevisionDeltas().values())
+ CommitData newCommitData = result.getNewCommitData();
+ if (newCommitData != null)
{
- ((InternalCDORevisionDelta)delta).adjustReferences(result.getReferenceAdjuster());
+ applyNewCommitData(newCommitData, timeStamp);
}
-
- for (CDOID id : getDetachedObjects().keySet())
- {
- removeObject(id);
- }
-
- CDOLockChangeInfo lockChangeInfo = makeUnlockChangeInfo(result);
-
- CDOCommitInfo commitInfo = makeCommitInfo(timeStamp, result.getPreviousTimeStamp());
- if (!commitInfo.isEmpty())
+ else
{
- InvalidationData invalidationData = new InvalidationData();
- invalidationData.setCommitInfo(commitInfo);
- invalidationData.setSender(transaction);
- invalidationData.setClearResourcePathCache(clearResourcePathCache);
- invalidationData.setSecurityImpact(result.getSecurityImpact());
- invalidationData.setNewPermissions(result.getNewPermissions());
- invalidationData.setLockChangeInfo(lockChangeInfo);
+ Map<CDOID, CDOObject> newObjects = getNewObjects();
+ postCommit(newObjects, result);
- session.invalidate(invalidationData);
- }
-
- // Bug 290032 - Sticky views
- if (session.isSticky())
- {
- CDOBranchPoint commitBranchPoint = CDOBranchUtil.copyBranchPoint(result);
+ Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
+ postCommit(dirtyObjects, result);
- // Note: keyset() does not work because ID mappings are not applied there!
- for (CDOObject object : newObjects.values())
+ for (CDORevisionDelta delta : getRevisionDeltas().values())
{
- session.setCommittedSinceLastRefresh(object.cdoID(), commitBranchPoint);
+ ((InternalCDORevisionDelta)delta).adjustReferences(result.getReferenceAdjuster());
}
- for (CDOID id : dirtyObjects.keySet())
+ for (CDOID id : getDetachedObjects().keySet())
{
- session.setCommittedSinceLastRefresh(id, commitBranchPoint);
+ removeObject(id);
}
- for (CDOID id : getDetachedObjects().keySet())
+ CDOLockChangeInfo lockChangeInfo = makeUnlockChangeInfo(result);
+
+ CDOCommitInfo commitInfo = makeCommitInfo(timeStamp, previousTimeStamp);
+ if (!commitInfo.isEmpty())
{
- session.setCommittedSinceLastRefresh(id, commitBranchPoint);
+ InvalidationData invalidationData = new InvalidationData();
+ invalidationData.setCommitInfo(commitInfo);
+ invalidationData.setSender(transaction);
+ invalidationData.setClearResourcePathCache(clearResourcePathCache);
+ invalidationData.setSecurityImpact(result.getSecurityImpact());
+ invalidationData.setNewPermissions(result.getNewPermissions());
+ invalidationData.setLockChangeInfo(lockChangeInfo);
+
+ session.invalidate(invalidationData);
}
- }
- CDOTransactionHandler2[] handlers = getTransactionHandlers2();
- for (int i = 0; i < handlers.length; i++)
- {
- CDOTransactionHandler2 handler = handlers[i];
- if (handler instanceof CDOTransactionHandler3)
+ // Bug 290032 - Sticky views
+ if (session.isSticky())
{
- CDOTransactionHandler3 handler3 = (CDOTransactionHandler3)handler;
- handler3.committedTransaction(transaction, this, commitInfo);
+ CDOBranchPoint commitBranchPoint = CDOBranchUtil.copyBranchPoint(result);
+
+ // Note: keySet() does not work because ID mappings are not applied there!
+ for (CDOObject object : newObjects.values())
+ {
+ session.setCommittedSinceLastRefresh(object.cdoID(), commitBranchPoint);
+ }
+
+ for (CDOID id : dirtyObjects.keySet())
+ {
+ session.setCommittedSinceLastRefresh(id, commitBranchPoint);
+ }
+
+ for (CDOID id : getDetachedObjects().keySet())
+ {
+ session.setCommittedSinceLastRefresh(id, commitBranchPoint);
+ }
}
- else
+
+ CDOTransactionHandler2[] handlers = getTransactionHandlers2();
+ for (int i = 0; i < handlers.length; i++)
{
- handler.committedTransaction(transaction, this);
+ CDOTransactionHandler2 handler = handlers[i];
+ if (handler instanceof CDOTransactionHandler3)
+ {
+ CDOTransactionHandler3 handler3 = (CDOTransactionHandler3)handler;
+ handler3.committedTransaction(transaction, this, commitInfo);
+ }
+ else
+ {
+ handler.committedTransaction(transaction, this);
+ }
}
- }
- getChangeSubscriptionManager().committedTransaction(transaction, this);
- getAdapterManager().committedTransaction(transaction, this);
+ getChangeSubscriptionManager().committedTransaction(transaction, this);
+ getAdapterManager().committedTransaction(transaction, this);
- cleanUp(this);
+ cleanUp(this);
- IListener[] listeners = getListeners();
- if (listeners != null)
- {
- if (branchChanged)
+ IListener[] listeners = getListeners();
+ if (listeners != null)
{
- fireViewTargetChangedEvent(oldBranch.getHead(), listeners);
- }
+ if (branchChanged)
+ {
+ fireViewTargetChangedEvent(oldBranch.getHead(), listeners);
+ }
- Map<CDOID, CDOID> idMappings = result.getIDMappings();
- fireEvent(new FinishedEvent(idMappings), listeners);
- }
+ Map<CDOID, CDOID> idMappings = result.getIDMappings();
+ fireEvent(new FinishedEvent(idMappings), listeners);
+ }
- if (lockChangeInfo != null && isActive())
- {
- fireLocksChangedEvent(CDOTransactionImpl.this, lockChangeInfo);
+ if (lockChangeInfo != null && isActive())
+ {
+ fireLocksChangedEvent(CDOTransactionImpl.this, lockChangeInfo);
+ }
}
}
catch (RuntimeException ex)
@@ -4817,6 +4838,12 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
CDOStateMachine.INSTANCE.commit(objects, result);
}
}
+
+ private void applyNewCommitData(CommitData newCommitData, long timeStamp)
+ {
+ getTransactionStrategy().rollback(CDOTransactionImpl.this, firstSavepoint); // Transitions objects to CLEAN.
+ waitForBaseline(timeStamp); // Transitions objects to PROXY.
+ }
}
/**
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
index 795f0a4c7d..528551e432 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOStateMachine.java
@@ -416,7 +416,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
try
{
- return writeWithoutViewLock(object, featureDelta);
+ return writeUnsynced(object, featureDelta);
}
finally
{
@@ -428,7 +428,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
}
}
- private Object writeWithoutViewLock(InternalCDOObject object, CDOFeatureDelta featureDelta)
+ public Object writeUnsynced(InternalCDOObject object, CDOFeatureDelta featureDelta)
{
if (TRACER.isEnabled())
{
@@ -1092,7 +1092,7 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
/**
* @author Eike Stepper
*/
- final private class CommitTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, CommitTransactionResult>
+ private final class CommitTransition implements ITransition<CDOState, CDOEvent, InternalCDOObject, CommitTransactionResult>
{
public CommitTransition(boolean useDeltas)
{
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
index 152478e279..10aec5b749 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java
@@ -2616,7 +2616,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv
*/
private static final class SubscribeEntry
{
- private InternalCDOObject object;
+ private final InternalCDOObject object;
private int count;
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 3b500a5eff..4d22773e6e 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
@@ -1002,6 +1002,8 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo
private Map<CDOID, CDOPermission> newPermissions;
+ private CommitData newCommitData;
+
/**
* @since 4.3
*/
@@ -1253,6 +1255,22 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo
newPermissions.put(id, permission);
}
+ /**
+ * @since 4.8
+ */
+ public CommitData getNewCommitData()
+ {
+ return newCommitData;
+ }
+
+ /**
+ * @since 4.8
+ */
+ public void setNewCommitData(CommitData newCommitData)
+ {
+ this.newCommitData = newCommitData;
+ }
+
protected PostCommitReferenceAdjuster createReferenceAdjuster()
{
return new PostCommitReferenceAdjuster(idProvider, new CDOIDMapper(idMappings));
diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java
index 487e354790..04764084b7 100644
--- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java
+++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java
@@ -960,7 +960,7 @@ public final class DBUtil
{
if (isTracerEnabled())
{
- trace(stmt.toString());
+ TRACER.trace(stmt.toString());
}
int result = stmt.executeUpdate();
diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java
index a68340249b..bbd7f7ba89 100644
--- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java
+++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java
@@ -97,7 +97,7 @@ public class DBPreparedStatement extends DelegatingPreparedStatement implements
@Override
public String toString()
{
- return "PreparedStatement[sql=" + sql + ", probability=" + reuseProbability + ", touch=" + touch + "]";
+ return getDelegate().toString();
}
@Override

Back to the top