Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/CDORevisionResolverImpl.java207
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContextImpl.java7
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java2
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/AbstractSyncRevisionsIndication.java120
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java45
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/SyncRevisionsIndication.java77
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java10
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java2
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java29
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_273565_Test.java270
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java2
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java3
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOMetaWrapper.java1
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java29
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/FailOverStrategyInjector.java12
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/AbstractSyncRevisionsRequest.java138
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CDOClientProtocol.java5
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CommitNotificationIndication.java2
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/LockObjectsRequest.java47
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/SyncRevisionsRequest.java109
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/query/CDOQueryImpl.java1
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDORevisionManagerImpl.java29
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java165
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java25
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java119
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java2
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java7
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java6
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.java18
30 files changed, 1087 insertions, 408 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/CDORevisionResolverImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/CDORevisionResolverImpl.java
index 88161216ff..01644e64cf 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/CDORevisionResolverImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/CDORevisionResolverImpl.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:
* Eike Stepper - initial API and implementation
* Simon McDuff - http://bugs.eclipse.org/201266
@@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.common.revision.cache.CDORevisionCacheUtil;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.trace.ContextTracer;
@@ -40,6 +41,12 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
private CDORevisionCache cache;
+ @ExcludeFromDump
+ private Object loadAndAddLock = new Object();
+
+ @ExcludeFromDump
+ private Object revisedLock = new Object();
+
public CDORevisionResolverImpl()
{
}
@@ -74,6 +81,56 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
return cache.getObjectType(id);
}
+ public void revisedRevision(CDOID id, long timeStamp)
+ {
+ acquireAtomicRequestLock(revisedLock);
+
+ try
+ {
+ InternalCDORevision revision = cache.getRevision(id);
+ if (revision != null)
+ {
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ removeCachedRevision(revision.getID(), revision.getVersion());
+ }
+ else
+ {
+ revision.setRevised(timeStamp - 1);
+ }
+ }
+ }
+ finally
+ {
+ releaseAtomicRequestLock(revisedLock);
+ }
+ }
+
+ public void revisedRevisionByVersion(CDOID id, int version, long timeStamp)
+ {
+ acquireAtomicRequestLock(revisedLock);
+
+ try
+ {
+ InternalCDORevision revision = cache.getRevisionByVersion(id, version);
+ if (revision != null)
+ {
+ if (timeStamp == CDORevision.UNSPECIFIED_DATE)
+ {
+ removeCachedRevision(revision.getID(), revision.getVersion());
+ }
+ else
+ {
+ revision.setRevised(timeStamp - 1);
+ }
+ }
+ }
+ finally
+ {
+ releaseAtomicRequestLock(revisedLock);
+ }
+ }
+
public InternalCDORevision getRevision(CDOID id, int referenceChunk)
{
return getRevision(id, referenceChunk, true);
@@ -81,31 +138,40 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
public InternalCDORevision getRevision(CDOID id, int referenceChunk, boolean loadOnDemand)
{
- InternalCDORevision revision = cache.getRevision(id);
- if (revision == null)
+ acquireAtomicRequestLock(loadAndAddLock);
+
+ try
{
- if (loadOnDemand)
+ InternalCDORevision revision = cache.getRevision(id);
+ if (revision == null)
{
- if (TRACER.isEnabled())
+ if (loadOnDemand)
{
- TRACER.format("Loading revision {0}", id); //$NON-NLS-1$
- }
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Loading revision {0}", id); //$NON-NLS-1$
+ }
- revision = loadRevision(id, referenceChunk);
- addCachedRevisionIfNotNull(revision);
+ revision = loadRevision(id, referenceChunk);
+ addCachedRevisionIfNotNull(revision);
+ }
}
- }
- else
- {
- InternalCDORevision oldRevision = revision;
- revision = verifyRevision(oldRevision, referenceChunk);
- if (revision != oldRevision)
+ else
{
- addCachedRevisionIfNotNull(revision);
+ InternalCDORevision oldRevision = revision;
+ revision = verifyRevision(oldRevision, referenceChunk);
+ if (revision != oldRevision)
+ {
+ addCachedRevisionIfNotNull(revision);
+ }
}
- }
- return revision;
+ return revision;
+ }
+ finally
+ {
+ releaseAtomicRequestLock(loadAndAddLock);
+ }
}
public InternalCDORevision getRevisionByTime(CDOID id, int referenceChunk, long timeStamp)
@@ -115,31 +181,40 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
public InternalCDORevision getRevisionByTime(CDOID id, int referenceChunk, long timeStamp, boolean loadOnDemand)
{
- InternalCDORevision revision = cache.getRevisionByTime(id, timeStamp);
- if (revision == null)
+ acquireAtomicRequestLock(loadAndAddLock);
+
+ try
{
- if (loadOnDemand)
+ InternalCDORevision revision = cache.getRevisionByTime(id, timeStamp);
+ if (revision == null)
{
- if (TRACER.isEnabled())
+ if (loadOnDemand)
{
- TRACER.format("Loading revision {0} by time {1,date} {1,time}", id, timeStamp); //$NON-NLS-1$
- }
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Loading revision {0} by time {1,date} {1,time}", id, timeStamp); //$NON-NLS-1$
+ }
- revision = loadRevisionByTime(id, referenceChunk, timeStamp);
- addCachedRevisionIfNotNull(revision);
+ revision = loadRevisionByTime(id, referenceChunk, timeStamp);
+ addCachedRevisionIfNotNull(revision);
+ }
}
- }
- else
- {
- InternalCDORevision verified = verifyRevision(revision, referenceChunk);
- if (revision != verified)
+ else
{
- addCachedRevisionIfNotNull(verified);
- revision = verified;
+ InternalCDORevision verified = verifyRevision(revision, referenceChunk);
+ if (revision != verified)
+ {
+ addCachedRevisionIfNotNull(verified);
+ revision = verified;
+ }
}
- }
- return revision;
+ return revision;
+ }
+ finally
+ {
+ releaseAtomicRequestLock(loadAndAddLock);
+ }
}
public synchronized InternalCDORevision getRevisionByVersion(CDOID id, int referenceChunk, int version)
@@ -149,22 +224,30 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
public InternalCDORevision getRevisionByVersion(CDOID id, int referenceChunk, int version, boolean loadOnDemand)
{
- InternalCDORevision revision = cache.getRevisionByVersion(id, version);
- if (revision == null)
+ acquireAtomicRequestLock(loadAndAddLock);
+
+ try
{
- if (loadOnDemand)
+ InternalCDORevision revision = cache.getRevisionByVersion(id, version);
+ if (revision == null)
{
- if (TRACER.isEnabled())
+ if (loadOnDemand)
{
- TRACER.format("Loading revision {0} by version {1}", id, version); //$NON-NLS-1$
- }
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Loading revision {0} by version {1}", id, version); //$NON-NLS-1$
+ }
- revision = loadRevisionByVersion(id, referenceChunk, version);
- addCachedRevisionIfNotNull(revision);
+ revision = loadRevisionByVersion(id, referenceChunk, version);
+ addCachedRevisionIfNotNull(revision);
+ }
}
+ return revision;
+ }
+ finally
+ {
+ releaseAtomicRequestLock(loadAndAddLock);
}
-
- return revision;
}
public List<CDORevision> getRevisions(Collection<CDOID> ids, int referenceChunk)
@@ -183,8 +266,17 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
if (!missingIDs.isEmpty())
{
- List<InternalCDORevision> missingRevisions = loadRevisions(missingIDs, referenceChunk);
- handleMissingRevisions(revisions, missingRevisions);
+ acquireAtomicRequestLock(loadAndAddLock);
+
+ try
+ {
+ List<InternalCDORevision> missingRevisions = loadRevisions(missingIDs, referenceChunk);
+ handleMissingRevisions(revisions, missingRevisions);
+ }
+ finally
+ {
+ releaseAtomicRequestLock(loadAndAddLock);
+ }
}
return revisions;
@@ -207,8 +299,17 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
if (missingIDs != null && !missingIDs.isEmpty())
{
- List<InternalCDORevision> missingRevisions = loadRevisionsByTime(missingIDs, referenceChunk, timeStamp);
- handleMissingRevisions(revisions, missingRevisions);
+ acquireAtomicRequestLock(loadAndAddLock);
+
+ try
+ {
+ List<InternalCDORevision> missingRevisions = loadRevisionsByTime(missingIDs, referenceChunk, timeStamp);
+ handleMissingRevisions(revisions, missingRevisions);
+ }
+ finally
+ {
+ releaseAtomicRequestLock(loadAndAddLock);
+ }
}
return revisions;
@@ -292,6 +393,14 @@ public abstract class CDORevisionResolverImpl extends Lifecycle implements CDORe
super.doDeactivate();
}
+ protected void acquireAtomicRequestLock(Object lockObject)
+ {
+ }
+
+ protected void releaseAtomicRequestLock(Object lockObject)
+ {
+ }
+
private void handleMissingRevisions(List<CDORevision> revisions, List<InternalCDORevision> missingRevisions)
{
Iterator<InternalCDORevision> it = missingRevisions.iterator();
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContextImpl.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContextImpl.java
index 4d8e182cad..ef986b5cae 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContextImpl.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContextImpl.java
@@ -43,6 +43,7 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -476,7 +477,11 @@ public class TransactionCommitContextImpl implements Transaction.InternalCommitC
}
}
}
-
+ if (!originObject.isCurrent())
+ {
+ throw new ConcurrentModificationException("Trying to update object " + dirtyObjectDelta.getID()
+ + " that was already modified");
+ }
InternalCDORevision dirtyObject = (InternalCDORevision)originObject.copy();
dirtyObjectDelta.apply(dirtyObject);
dirtyObject.setCreated(timeStamp);
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java
index f7c229a16b..987200dc69 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java
@@ -32,6 +32,7 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.ConcurrentModificationException;
import java.util.List;
/**
@@ -184,6 +185,11 @@ public class MEMStoreAccessor extends LongIDStoreAccessor
protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, long created)
{
InternalCDORevision revision = getStore().getRevision(revisionDelta.getID());
+ if (revision.getVersion() != revisionDelta.getOriginVersion())
+ {
+ throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID()
+ + " that was already modified");
+ }
InternalCDORevision newRevision = (InternalCDORevision)revision.copy();
revisionDelta.apply(newRevision);
newRevision.setCreated(created);
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java
index 52a2df2cb6..8f6f40c61e 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreChunkReader.java
@@ -40,7 +40,7 @@ public class MEMStoreChunkReader extends StoreChunkReader
for (Chunk chunk : chunks)
{
int startIndex = chunk.getStartIndex();
- InternalCDORevision revision = (InternalCDORevision)store.getRevision(getRevision().getID());
+ InternalCDORevision revision = store.getRevision(getRevision().getID());
for (int i = 0; i < chunk.size(); i++)
{
Object object = revision.get(getFeature(), startIndex + i);
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/AbstractSyncRevisionsIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/AbstractSyncRevisionsIndication.java
new file mode 100644
index 0000000000..4a06e81168
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/AbstractSyncRevisionsIndication.java
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Copyright (c) 2004-2007 Eike Stepper, Germany.
+ * 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.internal.server.protocol;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.io.CDODataInput;
+import org.eclipse.emf.cdo.common.io.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.internal.server.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.util.collection.Pair;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Simon McDuff
+ */
+public abstract class AbstractSyncRevisionsIndication extends CDOReadIndication
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, SyncRevisionsIndication.class);
+
+ protected List<Pair<InternalCDORevision, Long>> dirtyObjects = new ArrayList<Pair<InternalCDORevision, Long>>();
+
+ protected List<Pair<CDOID, Long>> detachedObjects = new ArrayList<Pair<CDOID, Long>>();
+
+ protected int referenceChunk = CDORevision.UNCHUNKED;
+
+ public AbstractSyncRevisionsIndication(CDOServerProtocol protocol, short signalID)
+ {
+ super(protocol, signalID);
+ }
+
+ @Override
+ protected void indicating(CDODataInput in) throws IOException
+ {
+ referenceChunk = in.readInt();
+ int size = in.readInt();
+ for (int i = 0; i < size; i++)
+ {
+ CDOID id = in.readCDOID();
+ int version = in.readInt();
+ process(id, version);
+ }
+ }
+
+ @Override
+ protected void responding(CDODataOutput out) throws IOException
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Sync found " + dirtyObjects.size() + " dirty objects");
+ }
+
+ out.writeInt(dirtyObjects.size());
+ for (Pair<InternalCDORevision, Long> revisionAndOldRevised : dirtyObjects)
+ {
+ out.writeCDORevision(revisionAndOldRevised.getElement1(), referenceChunk);
+ out.writeLong(revisionAndOldRevised.getElement2());
+ }
+
+ out.writeInt(detachedObjects.size());
+ for (Pair<CDOID, Long> idAndRevised : detachedObjects)
+ {
+ out.writeCDOID(idAndRevised.getElement1());
+ out.writeLong(idAndRevised.getElement2());
+ }
+ }
+
+ protected abstract void process(CDOID id, int version);
+
+ protected void udpateObjectList(CDOID id, int version)
+ {
+ try
+ {
+ InternalCDORevision revision = getRepository().getRevisionManager().getRevision(id, referenceChunk);
+ if (revision == null)
+ {
+ detachedObjects.add(new Pair<CDOID, Long>(id, getTimestamp(id, version)));
+ }
+ else if (revision.getVersion() > version || version == CDORevision.UNSPECIFIED_VERSION)
+ {
+ dirtyObjects.add(new Pair<InternalCDORevision, Long>(revision, getTimestamp(id, version)));
+ }
+ else if (revision.getVersion() < version)
+ {
+ throw new IllegalStateException("The object " + revision.getID() + " have a higher version ("
+ + revision.getVersion() + ") in the repository than the version (" + version + ") submitted.");
+ }
+ }
+ catch (IllegalArgumentException revisionIsNullException)
+ {
+ detachedObjects.add(new Pair<CDOID, Long>(id, getTimestamp(id, version)));
+ }
+
+ }
+
+ protected long getTimestamp(CDOID id, int version)
+ {
+ CDORevision revision = getRepository().getRevisionManager().getRevisionByVersion(id, 0, version, false);
+ if (revision != null)
+ {
+ return revision.getRevised() + 1;
+ }
+
+ return CDORevision.UNSPECIFIED_DATE;
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java
index 5ba8894fc0..e1690ddbc5 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java
@@ -12,6 +12,8 @@
package org.eclipse.emf.cdo.internal.server.protocol;
import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.io.CDODataInput;
import org.eclipse.emf.cdo.common.io.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
@@ -27,8 +29,16 @@ import java.util.List;
/**
* @author Simon McDuff
*/
-public class LockObjectsIndication extends CDOReadIndication
+public class LockObjectsIndication extends AbstractSyncRevisionsIndication
{
+ private RWLockManager.LockType lockType;
+
+ private List<CDOID> ids = new ArrayList<CDOID>();
+
+ private List<CDOIDAndVersion> idAndVersions = new ArrayList<CDOIDAndVersion>();
+
+ private IView view;
+
public LockObjectsIndication(CDOServerProtocol protocol)
{
super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS);
@@ -37,21 +47,15 @@ public class LockObjectsIndication extends CDOReadIndication
@Override
protected void indicating(CDODataInput in) throws IOException
{
+ super.indicating(in);
+
int viewID = in.readInt();
- RWLockManager.LockType lockType = in.readCDOLockType();
+ lockType = in.readCDOLockType();
long timeout = in.readLong();
- int size = in.readInt();
- List<CDOID> ids = new ArrayList<CDOID>(size);
- for (int i = 0; i < size; i++)
- {
- CDOID id = in.readCDOID();
- ids.add(id);
- }
-
try
{
- IView view = getSession().getView(viewID);
+ view = getSession().getView(viewID);
getRepository().getLockManager().lock(lockType, view, ids, timeout);
}
catch (InterruptedException ex)
@@ -63,6 +67,23 @@ public class LockObjectsIndication extends CDOReadIndication
@Override
protected void responding(CDODataOutput out) throws IOException
{
- out.writeBoolean(true);
+ for (CDOIDAndVersion idAndVersion : idAndVersions)
+ {
+ udpateObjectList(idAndVersion.getID(), idAndVersion.getVersion());
+ }
+
+ if (!detachedObjects.isEmpty())
+ {
+ getRepository().getLockManager().unlock(lockType, view, ids);
+ throw new IllegalArgumentException(detachedObjects.size() + " objects are not persistent anymore");
+ }
+ super.responding(out);
+ }
+
+ @Override
+ protected void process(CDOID id, int version)
+ {
+ ids.add(id);
+ idAndVersions.add(CDOIDUtil.createIDAndVersion(id, version));
}
}
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/SyncRevisionsIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/SyncRevisionsIndication.java
index ec4aa7d50e..f60d01432e 100644
--- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/SyncRevisionsIndication.java
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/SyncRevisionsIndication.java
@@ -13,34 +13,22 @@ package org.eclipse.emf.cdo.internal.server.protocol;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.io.CDODataInput;
-import org.eclipse.emf.cdo.common.io.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.internal.server.bundle.OM;
import org.eclipse.emf.cdo.server.IStoreAccessor;
import org.eclipse.emf.cdo.server.StoreThreadLocal;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.net4j.util.collection.Pair;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
/**
* @author Simon McDuff
*/
-public class SyncRevisionsIndication extends CDOReadIndication
+public class SyncRevisionsIndication extends AbstractSyncRevisionsIndication
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, SyncRevisionsIndication.class);
- private List<Pair<InternalCDORevision, Long>> dirtyObjects = new ArrayList<Pair<InternalCDORevision, Long>>();
-
- private List<Pair<CDOID, Long>> detachedObjects = new ArrayList<Pair<CDOID, Long>>();
-
- private int referenceChunk;
-
public SyncRevisionsIndication(CDOServerProtocol protocol)
{
super(protocol, CDOProtocolConstants.SIGNAL_SYNC_REVISIONS);
@@ -61,70 +49,17 @@ public class SyncRevisionsIndication extends CDOReadIndication
}
reader.refreshRevisions();
- referenceChunk = in.readInt();
- int size = in.readInt();
- for (int i = 0; i < size; i++)
- {
- CDOID id = in.readCDOID();
- int version = in.readInt();
- if (version > 0)
- {
- try
- {
- InternalCDORevision revision = getRepository().getRevisionManager().getRevision(id, referenceChunk);
- if (revision == null)
- {
- detachedObjects.add(new Pair<CDOID, Long>(id, getTimestamp(id, version)));
- }
- else if (revision.getVersion() > version)
- {
- dirtyObjects.add(new Pair<InternalCDORevision, Long>(revision, getTimestamp(id, version)));
- }
- else if (revision.getVersion() < version)
- {
- throw new IllegalStateException("The object " + revision.getID() + " have a higher version ("
- + revision.getVersion() + ") in the repository than the version (" + version + ") submitted.");
- }
- }
- catch (IllegalArgumentException revisionIsNullException)
- {
- detachedObjects.add(new Pair<CDOID, Long>(id, getTimestamp(id, version)));
- }
- }
- }
- }
-
- private long getTimestamp(CDOID id, int version)
- {
- CDORevision revision = getRepository().getRevisionManager().getRevisionByVersion(id, 0, version, false);
- if (revision != null)
- {
- return revision.getRevised() + 1;
- }
- return CDORevision.UNSPECIFIED_DATE;
+ super.indicating(in);
}
@Override
- protected void responding(CDODataOutput out) throws IOException
+ protected void process(CDOID id, int version)
{
- if (TRACER.isEnabled())
- {
- TRACER.format("Sync found " + dirtyObjects.size() + " dirty objects");
- }
-
- out.writeInt(dirtyObjects.size());
- for (Pair<InternalCDORevision, Long> revisionAndOldRevised : dirtyObjects)
+ if (version > 0)
{
- out.writeCDORevision(revisionAndOldRevised.getElement1(), referenceChunk);
- out.writeLong(revisionAndOldRevised.getElement2());
- }
-
- out.writeInt(detachedObjects.size());
- for (Pair<CDOID, Long> idAndRevised : detachedObjects)
- {
- out.writeCDOID(idAndRevised.getElement1());
- out.writeLong(idAndRevised.getElement2());
+ udpateObjectList(id, version);
}
}
+
}
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 3eab81c58c..dac59d046d 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
@@ -159,6 +159,16 @@ public interface IStoreAccessor extends IQueryHandler
* <p>
* <b>Note</b>: {@link IStoreAccessor#write(CommitContext, OMMonitor)} and {@link IStoreAccessor#commit(OMMonitor)}
* could be called from different threads.
+ * <p>
+ * <b>Note</b>: Implementors should detect if dirty write occurred. In this case it should throw an exception.
+ *
+ * <pre>
+ * if (revision.getVersion() != revisionDelta.getOriginVersion())
+ * {
+ * throw new ConcurrentModificationException(&quot;Trying to update object &quot; + revisionDelta.getID()
+ * + &quot; that was already modified&quot;);
+ * }
+ * </pre>
*
* @since 2.0
*/
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java
index c5873e6823..1ad89757dc 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsAllConfigs.java
@@ -48,6 +48,7 @@ import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_267352_Test;
import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_270429_Test;
import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_272861_Test;
import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_273233_Test;
+import org.eclipse.emf.cdo.tests.bugzilla.Bugzilla_273565_Test;
import org.eclipse.emf.cdo.tests.config.impl.ConfigTest;
import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite;
@@ -143,6 +144,7 @@ public abstract class AllTestsAllConfigs extends ConfigTestSuite
testClasses.add(Bugzilla_270429_Test.class);
testClasses.add(Bugzilla_272861_Test.class);
testClasses.add(Bugzilla_273233_Test.class);
+ testClasses.add(Bugzilla_273565_Test.class);
// TODO testClasses.add(NonCDOResourceTest.class);
// TODO testClasses.add(GeneratedEcoreTest.class);
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
index 6fcfe84655..5d1634ab7d 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java
@@ -188,6 +188,35 @@ public class LockingManagerTest extends AbstractCDOTest
assertEquals(true, cdoCompany2.cdoReadLock().isLockedByOthers());
}
+ public void testDetachedObjects() throws Exception
+ {
+ Company company = getModel1Factory().createCompany();
+
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource res = transaction.createResource("/res1");
+ res.getContents().add(company);
+ transaction.commit();
+
+ CDOTransaction transaction2 = session.openTransaction();
+ Company company2 = (Company)transaction2.getResource("/res1").getContents().get(0);
+ res.getContents().remove(0);
+
+ CDOObject cdoCompany2 = CDOUtil.getCDOObject(company2);
+ transaction.commit();
+ try
+ {
+ cdoCompany2.cdoReadLock().lock();
+ fail("Should have fail");
+ }
+ catch (IllegalArgumentException ex)
+ {
+
+ }
+ assertEquals(false, cdoCompany2.cdoReadLock().isLocked());
+ assertEquals(false, cdoCompany2.cdoReadLock().isLockedByOthers());
+ }
+
public void testWriteLockByOthers() throws Exception
{
Company company = getModel1Factory().createCompany();
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_273565_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_273565_Test.java
new file mode 100644
index 0000000000..2ed15dd435
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_273565_Test.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2004 - 2009 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
+ */
+package org.eclipse.emf.cdo.tests.bugzilla;
+
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.tests.AbstractCDOTest;
+import org.eclipse.emf.cdo.tests.model1.Order;
+import org.eclipse.emf.cdo.tests.model1.OrderDetail;
+import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOUtil;
+
+import junit.framework.Assert;
+
+/**
+ * Concurrency problem: attribute of enumeration type not updated correctly between two clients
+ * <p>
+ * See https://bugs.eclipse.org/273565
+ *
+ * @author Simon McDuff
+ */
+public class Bugzilla_273565_Test extends AbstractCDOTest
+{
+ /**
+ * Thread 1 : Update the value at 1 only when the value is at 2.<br>
+ * Thread 2 : Update the value at 3 and 2 only when the value is at 1.<br>
+ * Thread 1 will load objects... but at the same time will update remote changes... causing to not have the latest
+ * version.
+ */
+ public void testBugzilla_273565() throws Exception
+ {
+ final OrderDetail orderDetail = getModel1Factory().createOrderDetail();
+ final boolean done[] = new boolean[1];
+ final Exception exception[] = new Exception[1];
+ done[0] = false;
+ orderDetail.setPrice(2);
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource("/test1");
+ resource.getContents().add(orderDetail);
+ transaction.commit();
+
+ Runnable changeObjects = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ OrderDetail orderDetail2 = (OrderDetail)transaction.getObject(CDOUtil.getCDOObject(orderDetail).cdoID());
+
+ while (!done[0])
+ {
+ int counter = 0;
+ while (orderDetail2.getPrice() != 1 && !done[0])
+ {
+ if (counter++ >= 20)
+ {
+ throw new IllegalStateException("Object should have changed");
+ }
+
+ Thread.sleep(100);
+ }
+
+ orderDetail2.setPrice(3);
+ transaction.commit();
+ orderDetail2.setPrice(2);
+ transaction.commit();
+ }
+
+ transaction.close();
+ session.close();
+ }
+ catch (Exception ex)
+ {
+ exception[0] = ex;
+ }
+ }
+ };
+
+ new Thread(changeObjects).start();
+
+ for (int i = 0; i < 50 && exception[0] == null; i++)
+ {
+ orderDetail.setPrice(1);
+ transaction.commit();
+
+ int counter = 0;
+ while (orderDetail.getPrice() != 2)
+ {
+ if (counter++ >= 20)
+ {
+ throw new IllegalStateException("Object should have changed");
+ }
+
+ Thread.sleep(100);
+ }
+ }
+
+ done[0] = true;
+ if (exception[0] != null)
+ {
+ exception[0].printStackTrace();
+ Assert.fail(exception[0].getMessage());
+ }
+
+ session.close();
+ }
+
+ public void testBugzilla_273565_List() throws Exception
+ {
+ final OrderDetail orderDetail = getModel1Factory().createOrderDetail();
+ final Order order = getModel1Factory().createOrder();
+ final boolean done[] = new boolean[1];
+ final Exception exception[] = new Exception[1];
+ done[0] = false;
+ order.getOrderDetails().add(orderDetail);
+ orderDetail.setPrice(2);
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource("/test1");
+ resource.getContents().add(order);
+ transaction.commit();
+
+ Runnable changeObjects = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ OrderDetail orderDetail2 = (OrderDetail)transaction.getObject(CDOUtil.getCDOObject(orderDetail).cdoID());
+
+ while (!done[0])
+ {
+ int counter = 0;
+ while (orderDetail2.getPrice() != 1 && !done[0])
+ {
+ if (counter++ >= 100)
+ {
+ throw new IllegalStateException("Object should have changed");
+ }
+
+ Thread.sleep(100);
+ }
+ transaction.getLock().lock();
+ transaction.getLock().unlock();
+
+ orderDetail2.setPrice(3);
+
+ transaction.commit();
+ orderDetail2.getOrder().getOrderDetails().remove(1);
+ orderDetail2.setPrice(2);
+ transaction.commit();
+ }
+
+ transaction.close();
+ session.close();
+ }
+ catch (Exception ex)
+ {
+ exception[0] = ex;
+ }
+ }
+ };
+
+ new Thread(changeObjects).start();
+
+ for (int i = 0; i < 50 && exception[0] == null; i++)
+ {
+
+ orderDetail.setPrice(1);
+ CDOUtil.getCDOObject(orderDetail.getOrder()).cdoWriteLock().lock();
+ orderDetail.getOrder().getOrderDetails().add(getModel1Factory().createOrderDetail());
+ transaction.commit();
+
+ int counter = 0;
+ while (orderDetail.getPrice() != 2)
+ {
+ if (counter++ >= 100)
+ {
+ throw new IllegalStateException("Object should have changed");
+ }
+
+ Thread.sleep(100);
+ }
+ transaction.getLock().lock();
+ transaction.getLock().unlock();
+ }
+
+ done[0] = true;
+ if (exception[0] != null)
+ {
+ exception[0].printStackTrace();
+ Assert.fail(exception[0].getMessage());
+ }
+
+ session.close();
+ }
+
+ public void testBugzilla_273565_Lock() throws Exception
+ {
+ final OrderDetail orderDetail = getModel1Factory().createOrderDetail();
+ final boolean done[] = new boolean[1];
+ final Exception exception[] = new Exception[1];
+ done[0] = false;
+ orderDetail.setPrice(2);
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ CDOResource resource = transaction.createResource("/test1");
+ resource.getContents().add(orderDetail);
+ transaction.commit();
+
+ Runnable changeObjects = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ CDOSession session = openModel1Session();
+ CDOTransaction transaction = session.openTransaction();
+ OrderDetail orderDetail2 = (OrderDetail)transaction.getObject(CDOUtil.getCDOObject(orderDetail).cdoID());
+
+ while (!done[0])
+ {
+ CDOUtil.getCDOObject(orderDetail2).cdoWriteLock().lock();
+ orderDetail2.setPrice(3);
+ transaction.commit();
+ }
+
+ transaction.close();
+ session.close();
+ }
+ catch (Exception ex)
+ {
+ exception[0] = ex;
+ }
+ }
+ };
+
+ new Thread(changeObjects).start();
+
+ for (int i = 0; i < 50 && exception[0] == null; i++)
+ {
+ CDOUtil.getCDOObject(orderDetail).cdoWriteLock().lock();
+ orderDetail.setPrice(1);
+ transaction.commit();
+
+ }
+
+ done[0] = true;
+ if (exception[0] != null)
+ {
+ exception[0].printStackTrace();
+ Assert.fail(exception[0].getMessage());
+ }
+
+ session.close();
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java
index 9393edbadf..84a1b1f909 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java
@@ -18,7 +18,7 @@ import org.eclipse.net4j.util.concurrent.RWLockManager;
import java.util.concurrent.locks.Lock;
/**
- * TODO Simon: JavaDoc
+ * Once object got lock, it will not go in conflict mode or cannot be changed.
*
* @author Simon McDuff
* @since 2.0
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java
index f43e9f4bf3..9aad67fadd 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java
@@ -271,6 +271,9 @@ public interface CDOView extends CDOCommonView, INotifier, IOptionsContainer
*/
public int reload(CDOObject... objects);
+ /**
+ * Locks the given objects. Once the objects are locked, they will not be changed remotely or go in conflict state.
+ */
public void lockObjects(Collection<? extends CDOObject> objects, RWLockManager.LockType lockType, long timeout)
throws InterruptedException;
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOMetaWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOMetaWrapper.java
index e91ce718d1..19f6868a34 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOMetaWrapper.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOMetaWrapper.java
@@ -55,6 +55,7 @@ public class CDOMetaWrapper extends CDOObjectWrapper
throw new UnsupportedOperationException();
}
+ @Override
public EClass eClass()
{
throw new UnsupportedOperationException();
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java
index 7f3722c62d..dcb23d2963 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOStateMachine.java
@@ -297,6 +297,35 @@ public final class CDOStateMachine extends FiniteStateMachine<CDOState, CDOEvent
/**
* @since 2.0
*/
+ public InternalCDORevision readNoLoad(InternalCDOObject object)
+ {
+ ReentrantLock lock = lockView(object.cdoView());
+
+ try
+ {
+ switch (object.cdoState())
+ {
+ case TRANSIENT:
+ case PREPARED:
+ case NEW:
+ case CONFLICT:
+ case INVALID_CONFLICT:
+ case INVALID:
+ case PROXY:
+ return null;
+ }
+
+ return object.cdoRevision();
+ }
+ finally
+ {
+ unlockView(lock);
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
public void write(InternalCDOObject object)
{
write(object, null);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/FailOverStrategyInjector.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/FailOverStrategyInjector.java
index 4f87b4a266..c7280d31aa 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/FailOverStrategyInjector.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/FailOverStrategyInjector.java
@@ -52,22 +52,22 @@ public class FailOverStrategyInjector implements IElementProcessor
int pos = description.indexOf(SCHEME_SEPARATOR);
if (pos == -1)
{
- throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description,
- Messages.getString("FailOverStrategyInjector.0"))); //$NON-NLS-1$
+ throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description, Messages
+ .getString("FailOverStrategyInjector.0"))); //$NON-NLS-1$
}
String factoryType = description.substring(0, pos);
if (StringUtil.isEmpty(factoryType))
{
- throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description,
- Messages.getString("FailOverStrategyInjector.1"))); //$NON-NLS-1$
+ throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description, Messages
+ .getString("FailOverStrategyInjector.1"))); //$NON-NLS-1$
}
String connectorDescription = description.substring(pos + SCHEME_SEPARATOR.length());
if (StringUtil.isEmpty(connectorDescription))
{
- throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description,
- Messages.getString("FailOverStrategyInjector.2"))); //$NON-NLS-1$
+ throw new IllegalArgumentException(MessageFormat.format(INVALID_URI_MESSAGE, description, Messages
+ .getString("FailOverStrategyInjector.2"))); //$NON-NLS-1$
}
pos = connectorDescription.indexOf('?');
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/AbstractSyncRevisionsRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/AbstractSyncRevisionsRequest.java
new file mode 100644
index 0000000000..8f447ed197
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/AbstractSyncRevisionsRequest.java
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright (c) 2004 - 2009 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.internal.cdo.net4j.protocol;
+
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDAndVersion;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.io.CDODataInput;
+import org.eclipse.emf.cdo.common.io.CDODataOutput;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.messages.Messages;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.transaction.CDOTimeStampContext;
+
+import org.eclipse.emf.internal.cdo.bundle.OM;
+import org.eclipse.emf.internal.cdo.session.CDORevisionManagerImpl;
+import org.eclipse.emf.internal.cdo.transaction.CDOTimeStampContextImpl;
+
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * @author Simon McDuff
+ * @since 2.0
+ */
+public abstract class AbstractSyncRevisionsRequest extends CDOClientRequest<Collection<CDOTimeStampContext>>
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, AbstractSyncRevisionsRequest.class);
+
+ protected Map<CDOID, CDOIDAndVersion> idAndVersions;
+
+ protected int referenceChunk;
+
+ public AbstractSyncRevisionsRequest(CDOClientProtocol protocol, short signalID,
+ Map<CDOID, CDOIDAndVersion> idAndVersions, int referenceChunk)
+ {
+ super(protocol, signalID);
+ this.idAndVersions = idAndVersions;
+ this.referenceChunk = referenceChunk;
+ }
+
+ @Override
+ protected void requesting(CDODataOutput out) throws IOException
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Synchronization " + idAndVersions.size() + " objects"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ out.writeInt(referenceChunk);
+ out.writeInt(idAndVersions.size());
+ for (CDOIDAndVersion idAndVersion : idAndVersions.values())
+ {
+ out.writeCDOIDAndVersion(idAndVersion);
+ }
+ }
+
+ @Override
+ protected Collection<CDOTimeStampContext> confirming(CDODataInput in) throws IOException
+ {
+ CDORevisionManagerImpl revisionManager = (CDORevisionManagerImpl)getSession().getRevisionManager();
+ TreeMap<Long, CDOTimeStampContext> mapofContext = new TreeMap<Long, CDOTimeStampContext>();
+
+ int size = in.readInt();
+ for (int i = 0; i < size; i++)
+ {
+ CDORevision revision = in.readCDORevision();
+ long revised = in.readLong();
+
+ CDOIDAndVersion idAndVersion = idAndVersions.get(revision.getID());
+ if (idAndVersion == null)
+ {
+ throw new IllegalStateException(MessageFormat.format(
+ Messages.getString("SyncRevisionsRequest.2"), revision.getID())); //$NON-NLS-1$
+ }
+
+ Set<CDOIDAndVersion> dirtyObjects = getMap(mapofContext, revised).getDirtyObjects();
+ dirtyObjects.add(CDOIDUtil.createIDAndVersion(idAndVersion.getID(), idAndVersion.getVersion()));
+ revisionManager.addCachedRevision((InternalCDORevision)revision);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Synchronization received " + size + " dirty objects"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ size = in.readInt();
+ for (int i = 0; i < size; i++)
+ {
+ CDOID id = in.readCDOID();
+ long revised = in.readLong();
+
+ Collection<CDOID> detachedObjects = getMap(mapofContext, revised).getDetachedObjects();
+ detachedObjects.add(id);
+ }
+
+ for (CDOTimeStampContext timestampContext : mapofContext.values())
+ {
+ Set<CDOIDAndVersion> dirtyObjects = timestampContext.getDirtyObjects();
+ Collection<CDOID> detachedObjects = timestampContext.getDetachedObjects();
+
+ dirtyObjects = Collections.unmodifiableSet(dirtyObjects);
+ detachedObjects = Collections.unmodifiableCollection(detachedObjects);
+
+ ((CDOTimeStampContextImpl)timestampContext).setDirtyObjects(dirtyObjects);
+ ((CDOTimeStampContextImpl)timestampContext).setDetachedObjects(detachedObjects);
+ }
+ return Collections.unmodifiableCollection(mapofContext.values());
+ }
+
+ private CDOTimeStampContext getMap(Map<Long, CDOTimeStampContext> mapOfContext, long timestamp)
+ {
+ CDOTimeStampContext result = mapOfContext.get(timestamp);
+ if (result == null)
+ {
+ result = new CDOTimeStampContextImpl(timestamp);
+ mapOfContext.put(timestamp, result);
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CDOClientProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CDOClientProtocol.java
index 5dab60355d..7cd1517158 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CDOClientProtocol.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CDOClientProtocol.java
@@ -167,7 +167,7 @@ public class CDOClientProtocol extends CDOProtocolImpl implements CDOSessionProt
}
}
- public void lockObjects(CDOView view, Collection<? extends CDOObject> objects, long timeout, LockType lockType)
+ public void lockObjects(CDOView view, Map<CDOID, CDOIDAndVersion> objects, long timeout, LockType lockType)
throws InterruptedException
{
InterruptedException interruptedException = null;
@@ -175,7 +175,8 @@ public class CDOClientProtocol extends CDOProtocolImpl implements CDOSessionProt
try
{
- new LockObjectsRequest(this, view, objects, timeout, lockType).send();
+ new LockObjectsRequest(this, view, objects, view.getSession().options().getCollectionLoadingPolicy()
+ .getInitialChunkSize(), timeout, lockType).send();
}
catch (RemoteException ex)
{
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CommitNotificationIndication.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CommitNotificationIndication.java
index 02e39744e0..2b44719737 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CommitNotificationIndication.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/CommitNotificationIndication.java
@@ -55,7 +55,7 @@ public class CommitNotificationIndication extends CDOClientIndication
}
CDOPackageUnit[] packageUnits = in.readCDOPackageUnits(null);
- InternalCDOPackageRegistry packageRegistry = (InternalCDOPackageRegistry)getSession().getPackageRegistry();
+ InternalCDOPackageRegistry packageRegistry = getSession().getPackageRegistry();
for (int i = 0; i < packageUnits.length; i++)
{
packageRegistry.putPackageUnit((InternalCDOPackageUnit)packageUnits[i]);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/LockObjectsRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/LockObjectsRequest.java
index 2002179965..95532f4bcd 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/LockObjectsRequest.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/LockObjectsRequest.java
@@ -10,10 +10,12 @@
**************************************************************************/
package org.eclipse.emf.internal.cdo.net4j.protocol;
-import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.io.CDODataInput;
import org.eclipse.emf.cdo.common.io.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.transaction.CDOTimeStampContext;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.internal.cdo.bundle.OM;
@@ -21,13 +23,18 @@ import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.net4j.util.concurrent.RWLockManager;
import org.eclipse.net4j.util.om.trace.ContextTracer;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDOView;
+
import java.io.IOException;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
/**
* @author Simon McDuff
*/
-public class LockObjectsRequest extends CDOClientRequest<Object>
+public class LockObjectsRequest extends AbstractSyncRevisionsRequest
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LockObjectsRequest.class);
@@ -35,16 +42,14 @@ public class LockObjectsRequest extends CDOClientRequest<Object>
private RWLockManager.LockType lockType;
- private Collection<? extends CDOObject> objects;
-
private long timeout;
- public LockObjectsRequest(CDOClientProtocol protocol, CDOView view, Collection<? extends CDOObject> objects,
- long timeout, RWLockManager.LockType lockType)
+ public LockObjectsRequest(CDOClientProtocol protocol, CDOView view, Map<CDOID, CDOIDAndVersion> idAndVersions,
+ int referenceChunk, long timeout, RWLockManager.LockType lockType)
{
- super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS);
+ super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS, idAndVersions, referenceChunk);
this.view = view;
- this.objects = objects;
+
this.timeout = timeout;
this.lockType = lockType;
}
@@ -52,6 +57,7 @@ public class LockObjectsRequest extends CDOClientRequest<Object>
@Override
protected void requesting(CDODataOutput out) throws IOException
{
+ super.requesting(out);
out.writeInt(view.getViewID());
out.writeCDOLockType(lockType);
out.writeLong(timeout);
@@ -61,23 +67,20 @@ public class LockObjectsRequest extends CDOClientRequest<Object>
TRACER.format("Locking of type {0} requested for view {1} with timeout {2}", //$NON-NLS-1$
lockType == RWLockManager.LockType.READ ? "read" : "write", view.getViewID(), timeout); //$NON-NLS-1$ //$NON-NLS-2$
}
-
- out.writeInt(objects.size());
- for (CDOObject object : objects)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Locking requested for objects {0}", object.cdoID()); //$NON-NLS-1$
- }
-
- out.writeCDOID(object.cdoID());
- }
}
@Override
- protected Object confirming(CDODataInput in) throws IOException
+ protected Collection<CDOTimeStampContext> confirming(CDODataInput in) throws IOException
{
- in.readBoolean();
- return null;
+ Collection<CDOTimeStampContext> contexts = super.confirming(in);
+ for (CDOTimeStampContext timestampContext : contexts)
+ {
+ getSession().handleUpdateRevision(timestampContext.getTimeStamp(), timestampContext.getDirtyObjects(),
+ timestampContext.getDetachedObjects());
+ ((InternalCDOView)view).handleInvalidationWithoutNotification(timestampContext.getDirtyObjects(),
+ timestampContext.getDetachedObjects(), new HashSet<InternalCDOObject>(), new HashSet<InternalCDOObject>());
+ }
+
+ return contexts;
}
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/SyncRevisionsRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/SyncRevisionsRequest.java
index ef75bc11c9..65ed4b1fd1 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/SyncRevisionsRequest.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/net4j/protocol/SyncRevisionsRequest.java
@@ -13,41 +13,22 @@ package org.eclipse.emf.internal.cdo.net4j.protocol;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDAndVersion;
-import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.io.CDODataInput;
-import org.eclipse.emf.cdo.common.io.CDODataOutput;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.messages.Messages;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.transaction.CDOTimeStampContext;
-import org.eclipse.emf.internal.cdo.bundle.OM;
-import org.eclipse.emf.internal.cdo.session.CDORevisionManagerImpl;
-import org.eclipse.emf.internal.cdo.transaction.CDOTimeStampContextImpl;
-
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
import java.io.IOException;
-import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
/**
* @author Simon McDuff
* @since 2.0
*/
-public class SyncRevisionsRequest extends CDOClientRequest<Collection<CDOTimeStampContext>>
+public class SyncRevisionsRequest extends AbstractSyncRevisionsRequest
{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, SyncRevisionsRequest.class);
-
- private Map<CDOID, CDOIDAndVersion> idAndVersions;
-
- private int referenceChunk;
public SyncRevisionsRequest(CDOClientProtocol protocol, Map<CDOID, CDOIDAndVersion> idAndVersions, int referenceChunk)
{
@@ -57,95 +38,19 @@ public class SyncRevisionsRequest extends CDOClientRequest<Collection<CDOTimeSta
public SyncRevisionsRequest(CDOClientProtocol protocol, short signalID, Map<CDOID, CDOIDAndVersion> idAndVersions,
int referenceChunk)
{
- super(protocol, signalID);
- this.idAndVersions = idAndVersions;
- this.referenceChunk = referenceChunk;
- }
-
- @Override
- protected void requesting(CDODataOutput out) throws IOException
- {
- if (TRACER.isEnabled())
- {
- TRACER.trace("Synchronization " + idAndVersions.size() + " objects"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- out.writeInt(referenceChunk);
- out.writeInt(idAndVersions.size());
- for (CDOIDAndVersion idAndVersion : idAndVersions.values())
- {
- out.writeCDOID(idAndVersion.getID());
- out.writeInt(idAndVersion.getVersion());
- }
+ super(protocol, signalID, idAndVersions, referenceChunk);
}
@Override
protected Collection<CDOTimeStampContext> confirming(CDODataInput in) throws IOException
{
- CDORevisionManagerImpl revisionManager = (CDORevisionManagerImpl)getSession().getRevisionManager();
- TreeMap<Long, CDOTimeStampContext> mapofContext = new TreeMap<Long, CDOTimeStampContext>();
-
- int size = in.readInt();
- for (int i = 0; i < size; i++)
- {
- CDORevision revision = in.readCDORevision();
- long revised = in.readLong();
-
- CDOIDAndVersion idAndVersion = idAndVersions.get(revision.getID());
- if (idAndVersion == null)
- {
- throw new IllegalStateException(MessageFormat.format(
- Messages.getString("SyncRevisionsRequest.2"), revision.getID())); //$NON-NLS-1$
- }
-
- Set<CDOIDAndVersion> dirtyObjects = getMap(mapofContext, revised).getDirtyObjects();
- dirtyObjects.add(CDOIDUtil.createIDAndVersion(idAndVersion.getID(), idAndVersion.getVersion()));
- revisionManager.addCachedRevision((InternalCDORevision)revision);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.trace("Synchronization received " + size + " dirty objects"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- size = in.readInt();
- for (int i = 0; i < size; i++)
- {
- CDOID id = in.readCDOID();
- long revised = in.readLong();
-
- Collection<CDOID> detachedObjects = getMap(mapofContext, revised).getDetachedObjects();
- detachedObjects.add(id);
- }
-
+ Collection<CDOTimeStampContext> contexts = super.confirming(in);
Collection<CDOPackageUnit> emptyNewPackageUnits = Collections.emptyList();
- for (CDOTimeStampContext timestampContext : mapofContext.values())
- {
- Set<CDOIDAndVersion> dirtyObjects = timestampContext.getDirtyObjects();
- Collection<CDOID> detachedObjects = timestampContext.getDetachedObjects();
-
- dirtyObjects = Collections.unmodifiableSet(dirtyObjects);
- detachedObjects = Collections.unmodifiableCollection(detachedObjects);
-
- ((CDOTimeStampContextImpl)timestampContext).setDirtyObjects(dirtyObjects);
- ((CDOTimeStampContextImpl)timestampContext).setDetachedObjects(detachedObjects);
-
- getSession().handleSyncResponse(timestampContext.getTimeStamp(), emptyNewPackageUnits, dirtyObjects,
- detachedObjects);
- }
-
- return Collections.unmodifiableCollection(mapofContext.values());
- }
-
- private CDOTimeStampContext getMap(Map<Long, CDOTimeStampContext> mapOfContext, long timestamp)
- {
- CDOTimeStampContext result = mapOfContext.get(timestamp);
- if (result == null)
+ for (CDOTimeStampContext timestampContext : contexts)
{
- result = new CDOTimeStampContextImpl(timestamp);
- mapOfContext.put(timestamp, result);
+ getSession().handleSyncResponse(timestampContext.getTimeStamp(), emptyNewPackageUnits,
+ timestampContext.getDirtyObjects(), timestampContext.getDetachedObjects());
}
-
- return result;
+ return contexts;
}
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/query/CDOQueryImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/query/CDOQueryImpl.java
index 9616aa2ce5..b608377ec0 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/query/CDOQueryImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/query/CDOQueryImpl.java
@@ -34,6 +34,7 @@ import java.util.Map.Entry;
public class CDOQueryImpl extends CDOQueryInfoImpl implements CDOQuery
{
private static final String OBJECT_NOT_PERSISTED_MESSAGE = Messages.getString("CDOQueryImpl.0"); //$NON-NLS-1$
+
private InternalCDOView view;
public CDOQueryImpl(InternalCDOView view, String queryLanguage, String queryString)
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDORevisionManagerImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDORevisionManagerImpl.java
index 69d6ede7e6..d2022f9576 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDORevisionManagerImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDORevisionManagerImpl.java
@@ -19,12 +19,17 @@ import org.eclipse.emf.cdo.session.CDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.view.CDOFetchRuleManager;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.concurrent.RWLockManager;
+import org.eclipse.net4j.util.concurrent.RWLockManager.LockType;
+
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.spi.cdo.InternalCDOSession;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* @author Eike Stepper
@@ -35,6 +40,10 @@ public class CDORevisionManagerImpl extends CDORevisionResolverImpl implements C
private CDOFetchRuleManager ruleManager = CDOFetchRuleManager.NOOP;
+ private RWLockManager<CDORevisionManager, Object> lockmanager = new RWLockManager<CDORevisionManager, Object>();
+
+ private Set<CDORevisionManagerImpl> singletonCollection = Collections.singleton(this);
+
/**
* @since 2.0
*/
@@ -120,4 +129,24 @@ public class CDORevisionManagerImpl extends CDORevisionResolverImpl implements C
{
return session.getSessionProtocol().loadRevisionsByTime(ids, referenceChunk, timeStamp);
}
+
+ @Override
+ protected void acquireAtomicRequestLock(Object key)
+ {
+ try
+ {
+ lockmanager.lock(LockType.WRITE, key, this, RWLockManager.WAIT);
+ }
+ catch (InterruptedException ex)
+ {
+ throw WrappedException.wrap(ex);
+ }
+
+ }
+
+ @Override
+ protected void releaseAtomicRequestLock(Object key)
+ {
+ lockmanager.unlock(LockType.WRITE, key, singletonCollection);
+ }
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
index 7fbc8859e2..acfd7f689b 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
@@ -38,7 +38,6 @@ import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.session.CDOSessionInvalidationEvent;
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.transaction.CDOTimeStampContext;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;
@@ -68,7 +67,6 @@ import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDORemoteSessionManager;
import org.eclipse.emf.spi.cdo.InternalCDOSession;
import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
@@ -82,6 +80,7 @@ import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -406,103 +405,119 @@ public abstract class CDOSessionImpl extends Container<CDOView> implements Inter
private void handleCommitNotification(final long timeStamp, final Collection<CDOPackageUnit> newPackageUnits,
Set<CDOIDAndVersion> dirtyOIDs, final Collection<CDOID> detachedObjects,
final Collection<CDORevisionDelta> deltas, InternalCDOView excludedView, final boolean passiveUpdate,
- boolean async)
+ final boolean async)
{
- if (passiveUpdate)
+ try
{
- updateRevisionForRemoteChanges(timeStamp, dirtyOIDs, detachedObjects, excludedView);
- }
+ if (passiveUpdate)
+ {
+ updateRevisionForRemoteChanges(timeStamp, dirtyOIDs, detachedObjects, excludedView);
+ }
- final Set<CDOIDAndVersion> finalDirtyOIDs = Collections.unmodifiableSet(dirtyOIDs);
- final Collection<CDOID> finalDetachedObjects = Collections.unmodifiableCollection(detachedObjects);
- final boolean skipChangeSubscription = (deltas == null || deltas.size() <= 0)
- && (detachedObjects == null || detachedObjects.size() <= 0);
+ final Set<CDOIDAndVersion> finalDirtyOIDs = Collections.unmodifiableSet(dirtyOIDs);
+ final Collection<CDOID> finalDetachedObjects = Collections.unmodifiableCollection(detachedObjects);
+ final boolean skipChangeSubscription = (deltas == null || deltas.size() <= 0)
+ && (detachedObjects == null || detachedObjects.size() <= 0);
- for (final InternalCDOView view : getViews())
- {
- if (view != excludedView)
+ for (final InternalCDOView view : getViews())
{
- Runnable runnable = new Runnable()
+ if (view != excludedView)
{
- public void run()
+ Runnable runnable = new Runnable()
{
- try
+ public void run()
{
- Set<CDOObject> conflicts = null;
- if (passiveUpdate)
- {
- conflicts = view.handleInvalidation(timeStamp, finalDirtyOIDs, finalDetachedObjects);
- }
-
- if (!skipChangeSubscription)
+ try
{
- view.handleChangeSubscription(deltas, detachedObjects);
+ Set<CDOObject> conflicts = null;
+ if (passiveUpdate)
+ {
+ conflicts = view.handleInvalidation(timeStamp, finalDirtyOIDs, finalDetachedObjects);
+ }
+
+ if (!skipChangeSubscription)
+ {
+ view.handleChangeSubscription(deltas, detachedObjects);
+ }
+
+ if (conflicts != null)
+ {
+ ((InternalCDOTransaction)view).handleConflicts(conflicts);
+ }
}
-
- if (conflicts != null)
+ catch (RuntimeException ex)
{
- ((InternalCDOTransaction)view).handleConflicts(conflicts);
+ if (!async)
+ {
+ throw ex;
+ }
+
+ if (isActive())
+ {
+ OM.LOG.error(ex);
+ }
+ else
+ {
+ OM.LOG.info("Commit notification arrived while session is inactive");
+ }
}
}
- catch (RuntimeException ex)
- {
- OM.LOG.error(ex);
- }
- }
- };
+ };
- if (async)
- {
- QueueRunner runner = getInvalidationRunner();
- runner.addWork(runnable);
- }
- else
- {
- runnable.run();
+ if (async)
+ {
+ QueueRunner runner = getInvalidationRunner();
+ runner.addWork(runnable);
+ }
+ else
+ {
+ runnable.run();
+ }
}
}
}
+ catch (RuntimeException ex)
+ {
+ if (!async)
+ {
+ throw ex;
+ }
+
+ if (isActive())
+ {
+ OM.LOG.error(ex);
+ }
+ else
+ {
+ OM.LOG.info("Commit notification arrived while session is inactive");
+ }
+ }
fireInvalidationEvent(timeStamp, newPackageUnits, dirtyOIDs, detachedObjects, excludedView);
}
+ public void handleUpdateRevision(final long timeStamp, Set<CDOIDAndVersion> dirtyOIDs,
+ Collection<CDOID> detachedObjects)
+ {
+ updateRevisionForRemoteChanges(timeStamp, dirtyOIDs, detachedObjects, null);
+ }
+
private void updateRevisionForRemoteChanges(final long timeStamp, Set<CDOIDAndVersion> dirtyOIDs,
Collection<CDOID> detachedObjects, InternalCDOView excludedView)
{
- // revised is done automatically when postCommit is CDOTransaction.postCommit is happening
- // Detached are not revised through postCommit
if (excludedView == null || timeStamp == CDORevision.UNSPECIFIED_DATE)
{
for (CDOIDAndVersion dirtyOID : dirtyOIDs)
{
CDOID id = dirtyOID.getID();
int version = dirtyOID.getVersion();
- InternalCDORevision revision = revisionManager.getRevisionByVersion(id, 0, version, false);
- if (revision != null)
- {
- if (timeStamp == CDORevision.UNSPECIFIED_DATE)
- {
- revisionManager.removeCachedRevision(revision.getID(), revision.getVersion());
- }
- else
- {
- revision.setRevised(timeStamp - 1);
- }
- }
+ revisionManager.revisedRevisionByVersion(id, version, timeStamp);
}
}
for (CDOID id : detachedObjects)
{
- InternalCDORevision revision = revisionManager.getRevision(id, 0, false);
- if (timeStamp == CDORevision.UNSPECIFIED_DATE)
- {
- revisionManager.removeCachedRevision(revision.getID(), revision.getVersion());
- }
- else if (revision != null)
- {
- revision.setRevised(timeStamp - 1);
- }
+ revisionManager.revisedRevision(id, timeStamp);
}
}
@@ -742,27 +757,7 @@ public abstract class CDOSessionImpl extends Container<CDOView> implements Inter
Map<CDOID, CDOIDAndVersion> uniqueObjects = new HashMap<CDOID, CDOIDAndVersion>();
for (InternalCDOView view : getViews())
{
- Map<CDOID, CDORevisionDelta> deltaMap = view instanceof InternalCDOTransaction ? ((InternalCDOTransaction)view)
- .getRevisionDeltas() : null;
- for (InternalCDOObject internalCDOObject : view.getObjectsArray())
- {
- InternalCDORevision cdoRevision = internalCDOObject.cdoRevision();
- CDOID cdoId = internalCDOObject.cdoID();
- if (cdoRevision != null && !cdoId.isTemporary() && !uniqueObjects.containsKey(cdoId))
- {
- int version = cdoRevision.getVersion();
- if (deltaMap != null)
- {
- CDORevisionDelta delta = deltaMap.get(cdoId);
- if (delta != null)
- {
- version = delta.getOriginVersion();
- }
- }
-
- uniqueObjects.put(cdoId, CDOIDUtil.createIDAndVersion(cdoId, version));
- }
- }
+ view.getCDOIDAndVersion(uniqueObjects, Arrays.asList(view.getObjectsArray()));
}
// Need to add Revision from revisionManager since we do not have all objects in view.
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 901728748d..a323935063 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
@@ -285,6 +285,31 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa
conflict -= resolved;
}
+ @Override
+ public void getCDOIDAndVersion(Map<CDOID, CDOIDAndVersion> uniqueObjects, Collection<? extends CDOObject> cdoObjects)
+ {
+ Map<CDOID, CDORevisionDelta> deltaMap = getRevisionDeltas();
+
+ for (CDOObject cdoObject : cdoObjects)
+ {
+ CDORevision cdoRevision = CDOStateMachine.INSTANCE.readNoLoad((InternalCDOObject)cdoObject);
+ CDOID cdoId = cdoObject.cdoID();
+ if (cdoRevision != null && !cdoId.isTemporary() && !uniqueObjects.containsKey(cdoId))
+ {
+ int version = cdoRevision.getVersion();
+ if (deltaMap != null)
+ {
+ CDORevisionDelta delta = deltaMap.get(cdoId);
+ if (delta != null)
+ {
+ version = delta.getOriginVersion();
+ }
+ }
+ uniqueObjects.put(cdoId, CDOIDUtil.createIDAndVersion(cdoId, version));
+ }
+ }
+ }
+
/**
* @since 2.0
*/
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 9089b9c618..20e8bf313b 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
@@ -278,7 +278,18 @@ public class CDOViewImpl extends Lifecycle implements InternalCDOView
throws InterruptedException
{
checkActive();
- session.getSessionProtocol().lockObjects(this, objects, timeout, lockType);
+ Map<CDOID, CDOIDAndVersion> uniqueObjects = new HashMap<CDOID, CDOIDAndVersion>();
+ getCDOIDAndVersion(uniqueObjects, objects);
+ for (CDOObject object : objects)
+ {
+ CDOIDAndVersion idAndVersion = uniqueObjects.get(object.cdoID());
+ if (idAndVersion == null)
+ {
+ uniqueObjects
+ .put(object.cdoID(), CDOIDUtil.createIDAndVersion(object.cdoID(), CDORevision.UNSPECIFIED_VERSION));
+ }
+ }
+ session.getSessionProtocol().lockObjects(this, uniqueObjects, timeout, lockType);
}
/**
@@ -1101,49 +1112,7 @@ public class CDOViewImpl extends Lifecycle implements InternalCDOView
try
{
- for (CDOIDAndVersion dirtyOID : dirtyOIDs)
- {
- InternalCDOObject dirtyObject = null;
- // 258831 - Causes deadlock when introduce thread safe mechanisms in State machine.
- synchronized (objects)
- {
- dirtyObject = objects.get(dirtyOID.getID());
- }
-
- if (dirtyObject != null)
- {
- CDOStateMachine.INSTANCE.invalidate(dirtyObject, dirtyOID.getVersion());
- dirtyObjects.add(dirtyObject);
- if (dirtyObject.cdoConflict())
- {
- if (conflicts == null)
- {
- conflicts = new HashSet<CDOObject>();
- }
-
- conflicts.add(dirtyObject);
- }
- }
- }
-
- for (CDOID id : detachedOIDs)
- {
- InternalCDOObject detachedObject = removeObject(id);
- if (detachedObject != null)
- {
- CDOStateMachine.INSTANCE.detachRemote(detachedObject);
- detachedObjects.add(detachedObject);
- if (detachedObject.cdoConflict())
- {
- if (conflicts == null)
- {
- conflicts = new HashSet<CDOObject>();
- }
-
- conflicts.add(detachedObject);
- }
- }
- }
+ conflicts = handleInvalidationWithoutNotification(dirtyOIDs, detachedOIDs, dirtyObjects, detachedObjects);
}
finally
{
@@ -1176,6 +1145,54 @@ public class CDOViewImpl extends Lifecycle implements InternalCDOView
return conflicts;
}
+ public Set<CDOObject> handleInvalidationWithoutNotification(Set<CDOIDAndVersion> dirtyOIDs,
+ Collection<CDOID> detachedOIDs, Set<InternalCDOObject> dirtyObjects, Set<InternalCDOObject> detachedObjects)
+ {
+ Set<CDOObject> conflicts = null;
+ for (CDOIDAndVersion dirtyOID : dirtyOIDs)
+ {
+ InternalCDOObject dirtyObject = null;
+ // 258831 - Causes deadlock when introduce thread safe mechanisms in State machine.
+ synchronized (objects)
+ {
+ dirtyObject = objects.get(dirtyOID.getID());
+ }
+ if (dirtyObject != null)
+ {
+ CDOStateMachine.INSTANCE.invalidate(dirtyObject, dirtyOID.getVersion());
+ dirtyObjects.add(dirtyObject);
+ if (dirtyObject.cdoConflict())
+ {
+ if (conflicts == null)
+ {
+ conflicts = new HashSet<CDOObject>();
+ }
+
+ conflicts.add(dirtyObject);
+ }
+ }
+ }
+ for (CDOID id : detachedOIDs)
+ {
+ InternalCDOObject detachedObject = removeObject(id);
+ if (detachedObject != null)
+ {
+ CDOStateMachine.INSTANCE.detachRemote(detachedObject);
+ detachedObjects.add(detachedObject);
+ if (detachedObject.cdoConflict())
+ {
+ if (conflicts == null)
+ {
+ conflicts = new HashSet<CDOObject>();
+ }
+
+ conflicts.add(detachedObject);
+ }
+ }
+ }
+ return conflicts;
+ }
+
/**
* @since 2.0
*/
@@ -1404,6 +1421,20 @@ public class CDOViewImpl extends Lifecycle implements InternalCDOView
return getResourceSet();
}
+ public void getCDOIDAndVersion(Map<CDOID, CDOIDAndVersion> uniqueObjects, Collection<? extends CDOObject> cdoObjects)
+ {
+ for (CDOObject internalCDOObject : cdoObjects)
+ {
+ CDORevision cdoRevision = CDOStateMachine.INSTANCE.readNoLoad((InternalCDOObject)internalCDOObject);
+ CDOID cdoId = internalCDOObject.cdoID();
+ if (cdoRevision != null && !uniqueObjects.containsKey(cdoId))
+ {
+ int version = cdoRevision.getVersion();
+ uniqueObjects.put(cdoId, CDOIDUtil.createIDAndVersion(cdoId, version));
+ }
+ }
+ }
+
/**
* @since 2.0
*/
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 4ed399274e..d331743eee 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
@@ -82,7 +82,7 @@ public interface CDOSessionProtocol extends PackageLoader
public boolean cancelQuery(int queryId);
- public void lockObjects(CDOView view, Collection<? extends CDOObject> objects, long timeout, LockType lockType)
+ public void lockObjects(CDOView view, Map<CDOID, CDOIDAndVersion> objects, long timeout, LockType lockType)
throws InterruptedException;
public void unlockObjects(CDOView view, Collection<? extends CDOObject> objects, LockType lockType);
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
index e6a6296c05..d5b1b3d422 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java
@@ -46,4 +46,11 @@ public interface InternalCDOSession extends CDOSession, CDOIDObjectFactory,
public void handleSyncResponse(long timestamp, Collection<CDOPackageUnit> newPackageUnits,
Set<CDOIDAndVersion> dirtyOIDs, Collection<CDOID> detachedObjects);
+
+ /**
+ * In some cases we need to sync without propagating event. Lock is a good example.
+ */
+ public void handleUpdateRevision(final long timeStamp, Set<CDOIDAndVersion> dirtyOIDs,
+ Collection<CDOID> detachedObjects);
+
}
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
index 483246cb4f..7de9d04cb1 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java
@@ -29,6 +29,7 @@ import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.ecore.EObject;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
@@ -64,6 +65,11 @@ public interface InternalCDOView extends CDOView, CDOIDProvider, ILifecycle.Intr
public Set<CDOObject> handleInvalidation(long timeStamp, Set<CDOIDAndVersion> dirtyOIDs,
Collection<CDOID> detachedOIDs);
+ public Set<CDOObject> handleInvalidationWithoutNotification(Set<CDOIDAndVersion> dirtyOIDs,
+ Collection<CDOID> detachedOIDs, Set<InternalCDOObject> dirtyObjects, Set<InternalCDOObject> detachedObjects);
+
+ public void getCDOIDAndVersion(Map<CDOID, CDOIDAndVersion> uniqueObjects, Collection<? extends CDOObject> cdoObjects);
+
public void handleChangeSubscription(Collection<CDORevisionDelta> deltas, Collection<CDOID> detachedObjects);
public InternalCDOObject[] getObjectsArray();
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.java
index 27ab4873e1..ce63d47ede 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.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
@@ -97,7 +97,7 @@ public class RWLockManager<K, V> extends Lifecycle
private Object lockChanged = new Object();
- public void lock(RWLockManager.LockType type, V context, Collection<K> objectsToLock, long timeout)
+ public void lock(RWLockManager.LockType type, V context, Collection<? extends K> objectsToLock, long timeout)
throws InterruptedException
{
lock(getLockingStrategy(type), context, objectsToLock, timeout);
@@ -110,15 +110,13 @@ public class RWLockManager<K, V> extends Lifecycle
/**
* Attempts to release for a given locktype, context and objects.
- * <p>
- * .
*
* @throws IllegalMonitorStateException
* Unlocking objects without lock.
*/
- public void unlock(RWLockManager.LockType type, V context, Collection<K> objectToLock)
+ public void unlock(RWLockManager.LockType type, V context, Collection<? extends K> objectsToLock)
{
- unlock(getLockingStrategy(type), context, objectToLock);
+ unlock(getLockingStrategy(type), context, objectsToLock);
}
/**
@@ -193,7 +191,7 @@ public class RWLockManager<K, V> extends Lifecycle
* @throws IllegalMonitorStateException
* Unlocking object not locked.
*/
- private void unlock(LockStrategy<K, V> lockingStrategy, V context, Collection<K> objectsToLock)
+ private void unlock(LockStrategy<K, V> lockingStrategy, V context, Collection<? extends K> objectsToLock)
{
synchronized (lockChanged)
{
@@ -240,7 +238,7 @@ public class RWLockManager<K, V> extends Lifecycle
return entry != null && lockingStrategy.isLocked(entry, context);
}
- private void lock(LockStrategy<K, V> lockStrategy, V context, Collection<K> objectToLocks, long timeout)
+ private void lock(LockStrategy<K, V> lockStrategy, V context, Collection<? extends K> objectsToLocks, long timeout)
throws InterruptedException
{
long startTime = System.currentTimeMillis();
@@ -248,7 +246,7 @@ public class RWLockManager<K, V> extends Lifecycle
{
synchronized (lockChanged)
{
- K conflict = obtainLock(lockStrategy, context, objectToLocks);
+ K conflict = obtainLock(lockStrategy, context, objectsToLocks);
if (conflict == null)
{
lockChanged.notifyAll();
@@ -273,7 +271,7 @@ public class RWLockManager<K, V> extends Lifecycle
}
}
- private K obtainLock(LockStrategy<K, V> lockingStrategy, V context, Collection<K> objectsToLock)
+ private K obtainLock(LockStrategy<K, V> lockingStrategy, V context, Collection<? extends K> objectsToLock)
{
List<LockEntry<K, V>> lockEntrys = new ArrayList<LockEntry<K, V>>();
for (K objectToLock : objectsToLock)

Back to the top