diff options
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("Trying to update object " + revisionDelta.getID() + * + " that was already modified"); + * } + * </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) |