diff options
53 files changed, 2319 insertions, 58 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataInput.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataInput.java index 05e9789fbf..2c25147cb7 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataInput.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataInput.java @@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.io.ExtendedDataInput; import java.io.IOException; @@ -79,4 +80,8 @@ public interface CDODataInput extends ExtendedDataInput * Read either a CDORevision, a primitive value or a CDOClass. */ public Object readCDORevisionOrPrimitiveOrClass() throws IOException; + + // ///////////////////////////////////////////////////////////////////////////////////////////////// + + public RWLockManager.LockType readCDOLockType() throws IOException; } diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataOutput.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataOutput.java index df8e409b59..bf9754b973 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataOutput.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDODataOutput.java @@ -25,6 +25,7 @@ import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.io.ExtendedDataOutput; import java.io.IOException; @@ -80,4 +81,8 @@ public interface CDODataOutput extends ExtendedDataOutput * Write either a CDORevision, a primitive value or a CDOClass. */ public void writeCDORevisionOrPrimitiveOrClass(Object value) throws IOException; + + // ///////////////////////////////////////////////////////////////////////////////////////////////// + + public void writeCDOLockType(RWLockManager.LockType lockType) throws IOException; } diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java index a5dc508822..2f150171dc 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java @@ -106,6 +106,21 @@ public interface CDOProtocolConstants */ public static final short SIGNAL_REPOSITORY_TIME = 24; + /** + * @since 2.0 + */ + public static final short SIGNAL_LOCK_OBJECTS = 25; + + /** + * @since 2.0 + */ + public static final short SIGNAL_UNLOCK_OBJECTS = 26; + + /** + * @since 2.0 + */ + public static final short SIGNAL_OBJECT_LOCKED = 27; + // ////////////////////////////////////////////////////////////////////// // Session Management @@ -141,4 +156,12 @@ public interface CDOProtocolConstants * @since 2.0 */ public static final String QUERY_LANGUAGE_RESOURCES_EXACT_MATCH = "exactMatch"; + + // ////////////////////////////////////////////////////////////////////// + // Locking Objects + + /** + * @since 2.0 + */ + public static final int RELEASE_ALL_LOCKS = -1; } diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataInputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataInputImpl.java index 591c9934f4..7c638429d0 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataInputImpl.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataInputImpl.java @@ -59,6 +59,7 @@ import org.eclipse.emf.cdo.spi.common.InternalCDOList; import org.eclipse.emf.cdo.spi.common.InternalCDOPackage; import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.io.ExtendedDataInput; import org.eclipse.net4j.util.io.ExtendedIOUtil.ClassResolver; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -407,6 +408,11 @@ public abstract class CDODataInputImpl implements CDODataInput return readCDORevisionOrPrimitive(); } + public RWLockManager.LockType readCDOLockType() throws IOException + { + return readBoolean() ? RWLockManager.LockType.WRITE : RWLockManager.LockType.READ; + } + protected abstract CDOPackageManager getPackageManager(); protected abstract CDOPackageURICompressor getPackageURICompressor(); diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataOutputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataOutputImpl.java index c63288ddde..a183be4dd6 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataOutputImpl.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/CDODataOutputImpl.java @@ -38,6 +38,7 @@ import org.eclipse.emf.cdo.spi.common.InternalCDOClass; import org.eclipse.emf.cdo.spi.common.InternalCDOFeature; import org.eclipse.emf.cdo.spi.common.InternalCDOPackage; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.io.ExtendedDataOutput; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -368,5 +369,10 @@ public abstract class CDODataOutputImpl implements CDODataOutput } } + public void writeCDOLockType(RWLockManager.LockType lockType) throws IOException + { + writeBoolean(lockType == RWLockManager.LockType.WRITE ? true : false); + } + protected abstract CDOPackageURICompressor getPackageURICompressor(); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java new file mode 100644 index 0000000000..16048d5726 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/LockManager.java @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IRepositoryElement; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.ISessionManager; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.container.ContainerEventAdapter; +import org.eclipse.net4j.util.container.IContainer; +import org.eclipse.net4j.util.event.IListener; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class LockManager extends RWLockManager<CDOID, IView> implements IRepositoryElement +{ + private IRepository repository; + + @ExcludeFromDump + private transient IListener sessionListener = new ContainerEventAdapter<IView>() + { + @Override + protected void onRemoved(IContainer<IView> container, IView view) + { + unlock(view); + } + }; + + @ExcludeFromDump + private transient IListener sessionManagerListener = new ContainerEventAdapter<ISession>() + { + @Override + protected void onAdded(IContainer<ISession> container, ISession session) + { + session.addListener(sessionListener); + } + + @Override + protected void onRemoved(IContainer<ISession> container, ISession session) + { + session.removeListener(sessionListener); + } + }; + + public LockManager() + { + } + + public IRepository getRepository() + { + return repository; + } + + public void setRepository(IRepository repository) + { + this.repository = repository; + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + getRepository().getSessionManager().addListener(sessionManagerListener); + } + + @Override + protected void doDeactivate() throws Exception + { + ISessionManager sessionManager = getRepository().getSessionManager(); + sessionManager.removeListener(sessionManagerListener); + for (ISession session : sessionManager.getSessions()) + { + session.removeListener(sessionListener); + } + + super.doDeactivate(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java index d82bf41e4e..6b422a8d63 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java @@ -75,6 +75,8 @@ public class Repository extends Container<IRepositoryElement> implements IReposi private CommitManager commitManager; + private LockManager lockManager; + private IQueryHandlerProvider queryHandlerProvider; private List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); @@ -255,6 +257,22 @@ public class Repository extends Container<IRepositoryElement> implements IReposi /** * @since 2.0 */ + public LockManager getLockManager() + { + return lockManager; + } + + /** + * @since 2.0 + */ + public void setLockManager(LockManager lockManager) + { + this.lockManager = lockManager; + } + + /** + * @since 2.0 + */ public long createCommitTimeStamp() { long now = System.currentTimeMillis(); @@ -505,6 +523,7 @@ public class Repository extends Container<IRepositoryElement> implements IReposi checkState(queryManager, "queryManager"); checkState(notificationManager, "notificationManager"); checkState(commitManager, "commitManager"); + checkState(lockManager, "lockingManager"); packageManager.setRepository(this); sessionManager.setRepository(this); @@ -512,6 +531,7 @@ public class Repository extends Container<IRepositoryElement> implements IReposi queryManager.setRepository(this); notificationManager.setRepository(this); commitManager.setRepository(this); + lockManager.setRepository(this); checkState(store, "store"); supportingRevisionDeltas = store.getSupportedChangeFormats().contains(IStore.ChangeFormat.DELTA); @@ -536,7 +556,7 @@ public class Repository extends Container<IRepositoryElement> implements IReposi } elements = new IRepositoryElement[] { packageManager, sessionManager, revisionManager, queryManager, - notificationManager, commitManager, store }; + notificationManager, commitManager, lockManager, store }; } @Override @@ -559,11 +579,14 @@ public class Repository extends Container<IRepositoryElement> implements IReposi LifecycleUtil.activate(notificationManager); LifecycleUtil.activate(commitManager); LifecycleUtil.activate(queryHandlerProvider); + LifecycleUtil.activate(lockManager); + } @Override protected void doDeactivate() throws Exception { + LifecycleUtil.deactivate(lockManager); LifecycleUtil.deactivate(queryHandlerProvider); LifecycleUtil.deactivate(commitManager); LifecycleUtil.deactivate(notificationManager); @@ -619,6 +642,11 @@ public class Repository extends Container<IRepositoryElement> implements IReposi setCommitManager(createCommitManager()); } + if (getLockManager() == null) + { + setLockManager(createLockManager()); + } + super.doBeforeActivate(); } @@ -651,5 +679,10 @@ public class Repository extends Container<IRepositoryElement> implements IReposi { return new CommitManager(); } + + protected LockManager createLockManager() + { + return new LockManager(); + } } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java index 4a1dd2d55c..7ca27c950d 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java @@ -74,6 +74,18 @@ public class SessionManager extends Container<ISession> implements ISessionManag } } + /** + * @since 2.0 + */ + public Session getSession(int sessionID) + { + checkActive(); + synchronized (sessions) + { + return sessions.get(sessionID); + } + } + public ISession[] getElements() { return getSessions(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java index 1dc3bff8d9..ee14b178ce 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java @@ -31,6 +31,8 @@ import java.util.List; */ public class Transaction extends View implements ITransaction { + private boolean autoReleaseLocksEnabled = true; + public Transaction(Session session, int viewID) { super(session, viewID); @@ -117,5 +119,10 @@ public class Transaction extends View implements ITransaction public void setDirtyObjectDeltas(CDORevisionDelta[] dirtyObjectDeltas); public void setDetachedObjects(CDOID[] detachedObjects); + + public boolean setAutoReleaseLocksEnabled(boolean on); + + public boolean isAutoReleaseLocksEnabled(); } + } 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 3181762b78..a2dc0c11da 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 @@ -33,6 +33,7 @@ import org.eclipse.emf.cdo.spi.common.InternalCDORevisionDelta; import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -66,6 +67,8 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex private CDOID[] detachedObjects; + private List<CDOID> lockedObjects = new ArrayList<CDOID>(); + private List<InternalCDORevision> detachedRevisions = new ArrayList<InternalCDORevision>();; private CDORevisionDelta[] dirtyObjectDeltas; @@ -80,6 +83,8 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex private Transaction transaction; + private boolean autoReleaseLocksEnabled; + public TransactionCommitContextImpl(Transaction transaction) { this.transaction = transaction; @@ -199,6 +204,23 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex this.detachedObjects = detachedObjects; } + public boolean setAutoReleaseLocksEnabled(boolean on) + { + try + { + return autoReleaseLocksEnabled; + } + finally + { + autoReleaseLocksEnabled = on; + } + } + + public boolean isAutoReleaseLocksEnabled() + { + return autoReleaseLocksEnabled; + } + public void commit() { try @@ -232,6 +254,7 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex Repository repository = (Repository)transaction.getRepository(); computeDirtyObjects(!repository.isSupportingRevisionDeltas()); + lockObjects(); repository.notifyWriteAccessHandlers(transaction, this); detachObjects(); @@ -241,6 +264,10 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex { handleException(ex); } + catch (InterruptedException ex) + { + handleException(ex); + } catch (Error ex) { handleException(ex); @@ -331,6 +358,29 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex metaIDRanges.add(newRange); } + private void lockObjects() throws InterruptedException + { + lockedObjects.clear(); + for (int i = 0; i < dirtyObjectDeltas.length; i++) + { + lockedObjects.add(dirtyObjectDeltas[i].getID()); + } + + for (int i = 0; i < detachedObjects.length; i++) + { + lockedObjects.add(detachedObjects[i]); + } + + LockManager lockManager = ((Repository)transaction.getRepository()).getLockManager(); + lockManager.lock(RWLockManager.LockType.WRITE, transaction, lockedObjects, 1000); + } + + private void unlockObjects() + { + LockManager lockManager = ((Repository)transaction.getRepository()).getLockManager(); + lockManager.unlock(RWLockManager.LockType.WRITE, transaction, lockedObjects); + } + private void computeDirtyObjects(boolean failOnNull) { for (int i = 0; i < dirtyObjectDeltas.length; i++) @@ -364,7 +414,6 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex private void applyIDMappings(CDORevision[] revisions) { - for (CDORevision revision : revisions) { if (revision != null) @@ -383,6 +432,7 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex protected void rollback() { + unlockObjects(); if (accessor != null) { try @@ -404,6 +454,12 @@ public class TransactionCommitContextImpl implements IStoreAccessor.CommitContex addRevisions(newObjects); addRevisions(dirtyObjects); revisedDetachObjects(); + unlockObjects(); + + if (isAutoReleaseLocksEnabled()) + { + ((Repository)transaction.getRepository()).getLockManager().unlock(transaction); + } } catch (RuntimeException ex) { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java index c608131120..7346e29f2e 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java @@ -115,6 +115,15 @@ public class CDOServerProtocol extends CDOProtocolImpl case CDOProtocolConstants.SIGNAL_REPOSITORY_TIME: return new RepositoryTimeIndication(this); + case CDOProtocolConstants.SIGNAL_LOCK_OBJECTS: + return new LockObjectsIndication(this); + + case CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS: + return new UnlockObjectsIndication(this); + + case CDOProtocolConstants.SIGNAL_OBJECT_LOCKED: + return new ObjectLockedIndication(this); + default: return null; } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CommitTransactionIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CommitTransactionIndication.java index 4d5ae47888..55046fd7c2 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CommitTransactionIndication.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CommitTransactionIndication.java @@ -114,6 +114,9 @@ public class CommitTransactionIndication extends CDOServerIndication indicationTransaction(in); commitContext.preCommit(); + boolean autoReleaseLocksEnabled = in.readBoolean(); + commitContext.setAutoReleaseLocksEnabled(autoReleaseLocksEnabled); + TransactionPackageManager packageManager = commitContext.getPackageManager(); CDOPackage[] newPackages = new CDOPackage[in.readInt()]; CDORevision[] newObjects = new CDORevision[in.readInt()]; 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 new file mode 100644 index 0000000000..57824da787 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/LockObjectsIndication.java @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server.protocol; + +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.io.IORuntimeException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Simon McDuff + */ +public class LockObjectsIndication extends CDOReadIndication +{ + public LockObjectsIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int viewID = in.readInt(); + RWLockManager.LockType 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); + getRepository().getLockManager().lock(lockType, view, ids, timeout); + } + catch (InterruptedException ex) + { + throw new IORuntimeException(ex); + } + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + out.writeBoolean(true); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ObjectLockedIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ObjectLockedIndication.java new file mode 100644 index 0000000000..c85ce288f0 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ObjectLockedIndication.java @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server.protocol; + +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.concurrent.RWLockManager; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class ObjectLockedIndication extends CDOReadIndication +{ + private boolean isLocked; + + public ObjectLockedIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_OBJECT_LOCKED); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int viewID = in.readInt(); + IView view = getSession().getView(viewID); + + RWLockManager.LockType lockType = in.readCDOLockType(); + CDOID id = in.readCDOID(); + isLocked = getRepository().getLockManager().hasLock(lockType, view, id); + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + out.writeBoolean(isLocked); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/UnlockObjectsIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/UnlockObjectsIndication.java new file mode 100644 index 0000000000..418478c8f5 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/UnlockObjectsIndication.java @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server.protocol; + +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.concurrent.RWLockManager; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Simon McDuff + */ +public class UnlockObjectsIndication extends CDOReadIndication +{ + public UnlockObjectsIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int viewID = in.readInt(); + RWLockManager.LockType lockType = in.readCDOLockType(); + int size = in.readInt(); + + IView view = getSession().getView(viewID); + if (size == CDOProtocolConstants.RELEASE_ALL_LOCKS) + { + getRepository().getLockManager().unlock(view); + } + else + { + List<CDOID> ids = new ArrayList<CDOID>(size); + for (int i = 0; i < size; i++) + { + CDOID id = in.readCDOID(); + ids.add(id); + } + + getRepository().getLockManager().unlock(lockType, view, ids); + } + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + out.writeBoolean(true); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java index aa43b70ed1..24f513a4c7 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ISessionManager.java @@ -23,6 +23,11 @@ public interface ISessionManager extends IRepositoryElement, IContainer<ISession public ISession[] getSessions(); /** + * @since 2.0 + */ + public ISession getSession(int sessionID); + + /** * @return Never <code>null</code> * @since 2.0 */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java index 28cf794d7e..d5fdb3e453 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/ITransaction.java @@ -23,4 +23,5 @@ public interface ITransaction extends IView * Returns the ID of this transactional view. Same as {@link CDOProtocolView#getViewID() getViewID()}. */ public int getTransactionID(); + } diff --git a/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOAutomaticPackageRefTest.java b/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOAutomaticPackageRefTest.java index cc537c682d..5f2083b113 100644 --- a/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOAutomaticPackageRefTest.java +++ b/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOAutomaticPackageRefTest.java @@ -6,6 +6,8 @@ import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.StoreRepositoryProvider; +import org.eclipse.net4j.util.io.IOUtil; + import base.BaseFactory; import base.BasePackage; import derived.DerivedPackage; @@ -35,10 +37,10 @@ public class HbCDOAutomaticPackageRefTest extends AbstractCDOTest session.getPackageRegistry().putEPackage(DerivedPackage.eINSTANCE); transaction.commit(); } - catch (Exception e) + catch (Exception ex) { - e.printStackTrace(); - throw e; + IOUtil.print(ex); + throw ex; } } @@ -56,10 +58,10 @@ public class HbCDOAutomaticPackageRefTest extends AbstractCDOTest session.getPackageRegistry().putEPackage(DerivedPackage.eINSTANCE); transaction.commit(); } - catch (Exception e) + catch (Exception ex) { - e.printStackTrace(); - throw e; + IOUtil.print(ex); + throw ex; } } @@ -76,10 +78,10 @@ public class HbCDOAutomaticPackageRefTest extends AbstractCDOTest resource.getContents().add(BaseFactory.eINSTANCE.createBaseClass()); transaction.commit(); } - catch (Exception e) + catch (Exception ex) { - e.printStackTrace(); - throw e; + IOUtil.print(ex); + throw ex; } } @@ -96,10 +98,10 @@ public class HbCDOAutomaticPackageRefTest extends AbstractCDOTest resource.getContents().add(ReferenceFactory.eINSTANCE.createReference()); transaction.commit(); } - catch (Exception e) + catch (Exception ex) { - e.printStackTrace(); - throw e; + IOUtil.print(ex); + throw ex; } } } diff --git a/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOPackageRefTest.java b/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOPackageRefTest.java index bfe54a1709..cc152607d4 100644 --- a/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOPackageRefTest.java +++ b/plugins/org.eclipse.emf.cdo.tests.hibernate/src/org/eclipse/emf/cdo/tests/hibernate/HbCDOPackageRefTest.java @@ -6,6 +6,8 @@ import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.StoreRepositoryProvider; +import org.eclipse.net4j.util.io.IOUtil; + import reference.ReferenceFactory; import reference.ReferencePackage; import interface_.InterfacePackage; @@ -32,10 +34,10 @@ public class HbCDOPackageRefTest extends AbstractCDOTest resource.getContents().add(ReferenceFactory.eINSTANCE.createReference()); transaction.commit(); } - catch (Exception e) + catch (Exception ex) { - e.printStackTrace(); - throw e; + IOUtil.print(ex); + throw ex; } } } 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 5d6589233d..d4f84abfdc 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 @@ -73,6 +73,7 @@ public abstract class AllTestsAllConfigs extends ConfigTestSuite testClasses.add(XATransactionTest.class); testClasses.add(TransactionHandlerTest.class); testClasses.add(RepositoryTest.class); + testClasses.add(LockingManagerTest.class); // Specific for MEMStore testClasses.add(MEMStoreQueryTest.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 new file mode 100644 index 0000000000..4e3e761101 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingManagerTest.java @@ -0,0 +1,563 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper() throws Exception{} 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() throws Exception{} 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; + +import org.eclipse.emf.cdo.CDOLock; +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOSession; +import org.eclipse.emf.cdo.CDOTransaction; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.util.CDOUtil; + +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.transaction.TransactionException; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * @author Simon McDuff + */ +public class LockingManagerTest extends AbstractCDOTest +{ + public void testBasicUpgradeFromReadToWriteLock() throws Exception + { + final RWLockManager<Integer, Integer> lockingManager = new RWLockManager<Integer, Integer>(); + + Runnable step1 = new Runnable() + { + public void run() + { + Set<Integer> keys = new HashSet<Integer>(); + keys.add(1); + try + { + lockingManager.lock(RWLockManager.LockType.WRITE, 1, keys, 50000); + } + catch (InterruptedException ex) + { + fail("Should not have exception"); + } + } + }; + + ExecutorService executors = Executors.newFixedThreadPool(10); + Set<Integer> keys = new HashSet<Integer>(); + keys.add(1); + keys.add(2); + keys.add(3); + keys.add(4); + + msg("Context 1 have readlock 1,2,3,4"); + lockingManager.lock(RWLockManager.LockType.READ, 1, keys, 1000); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 1, 1)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 1, 2)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 1, 3)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 1, 4)); + + keys.clear(); + keys.add(1); + keys.add(2); + keys.add(3); + msg("Context 2 have readlock 1,2,3"); + lockingManager.lock(RWLockManager.LockType.READ, 2, keys, 1000); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 2, 1)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 2, 2)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 2, 3)); + + keys.clear(); + keys.add(4); + msg("Context 1 have readlock 1,2,3,4 and writeLock 4"); + lockingManager.lock(RWLockManager.LockType.WRITE, 1, keys, 1000); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.READ, 1, 4)); + assertEquals(true, lockingManager.hasLock(RWLockManager.LockType.WRITE, 1, 4)); + + keys.clear(); + keys.add(1); + try + { + lockingManager.lock(RWLockManager.LockType.WRITE, 1, keys, 1000); + fail("Should not have exception"); + } + catch (RuntimeException expected) + { + } + + executors.execute(step1); + executors.execute(step1); + + Thread.sleep(1000); + + keys.clear(); + keys.add(1); + keys.add(2); + keys.add(3); + lockingManager.unlock(RWLockManager.LockType.READ, 2, keys); + ITimeOuter timeOuter = new PollingTimeOuter(200, 100) + { + @Override + protected boolean successful() + { + return lockingManager.hasLock(RWLockManager.LockType.WRITE, 1, 1); + } + }; + + assertEquals(false, timeOuter.timedOut()); + } + + public void testBasicWrongUnlock() throws Exception + { + final RWLockManager<Integer, Integer> lockingManager = new RWLockManager<Integer, Integer>(); + Set<Integer> keys = new HashSet<Integer>(); + keys.add(1); + lockingManager.lock(RWLockManager.LockType.READ, 1, keys, 10000); + lockingManager.unlock(RWLockManager.LockType.READ, 1, keys); + try + { + lockingManager.unlock(RWLockManager.LockType.READ, 1, keys); + fail("Should have an exception"); + } + catch (IllegalMonitorStateException exception) + { + } + } + + public void testReadTimeout() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + + CDOTransaction transaction2 = session.openTransaction(); + CDOObject company2 = transaction2.getObject(cdoCompany.cdoID()); + + long start = System.currentTimeMillis(); + assertEquals(false, company2.cdoWriteLock().tryLock()); + assertEquals(true, System.currentTimeMillis() - start < 300); + + start = System.currentTimeMillis(); + assertEquals(false, company2.cdoWriteLock().tryLock(2, TimeUnit.SECONDS)); + assertEquals(true, System.currentTimeMillis() - start > 2000); + } + + public void testWriteLock() 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); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + CDOObject cdoCompany2 = CDOUtil.getCDOObject(company2); + + transaction.lockObjects(Collections.singletonList(cdoCompany), RWLockManager.LockType.WRITE, CDOLock.WAIT); + + try + { + transaction2.lockObjects(Collections.singletonList(cdoCompany2), RWLockManager.LockType.WRITE, 1000); + fail("Should have an exception"); + } + catch (TimeoutRuntimeException ex) + { + } + company2.setCity("Ottawa"); + try + { + transaction2.commit(); + fail("Should have an exception"); + } + catch (TransactionException exception) + { + } + } + + public void testWriteLockFromDifferenceTransaction() 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); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + CDOObject cdoCompany2 = CDOUtil.getCDOObject(company2); + + transaction.lockObjects(Collections.singletonList(cdoCompany), RWLockManager.LockType.WRITE, CDOLock.WAIT); + + try + { + transaction2.lockObjects(Collections.singletonList(cdoCompany2), RWLockManager.LockType.WRITE, 1000); + fail("Should have an exception"); + } + catch (TimeoutRuntimeException ex) + { + } + } + + public void testReadLockAndCommitFromDifferenceTransaction() 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); + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + + transaction.lockObjects(Collections.singletonList(cdoCompany), RWLockManager.LockType.READ, CDOLock.WAIT); + company2.setCity("Ottawa"); + + try + { + transaction2.commit(); + fail("Should have an exception"); + } + catch (TransactionException exception) + { + } + } + + public void testWriteLockAndCommitFromDifferenceTransaction() 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); + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + + transaction.lockObjects(Collections.singletonList(cdoCompany), RWLockManager.LockType.WRITE, CDOLock.WAIT); + company2.setCity("Ottawa"); + + try + { + transaction2.commit(); + fail("Should have an exception"); + } + catch (TransactionException expected) + { + IOUtil.print(expected); + } + } + + public void testReadLockAndCommitSameTransaction() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoReadLock().lock(); + + company.setCity("Ottawa"); + transaction.commit(); + } + + public void testWriteLockAndCommitSameTransaction() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + + company.setCity("Ottawa"); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + + transaction.commit(); + + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + } + + public void testWriteLockAndRollback() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + company.setCity("Ottawa"); + + transaction.rollback(); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + } + + public void testLockUnlock() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().unlock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().unlock(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().unlock(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().unlock(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + + /********************/ + + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().unlock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoWriteLock().unlock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().unlock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + + cdoCompany.cdoReadLock().unlock(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + assertEquals(false, cdoCompany.cdoWriteLock().isLocked()); + } + + public void testTransactionClose() throws Exception + { + Company company = getModel1Factory().createCompany(); + + Repository repo = (Repository)getRepository(); + CDOSession session = openModel1Session(); + + CDOTransaction transaction = session.openTransaction(); + IView view = repo.getSessionManager().getSession(session.getSessionID()).getView(transaction.getViewID()); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoReadLock().lock(); + transaction.close(); + assertEquals(false, repo.getLockManager().hasLock(RWLockManager.LockType.READ, view, cdoCompany.cdoID())); + } + + public void testSessionClose() throws Exception + { + Company company = getModel1Factory().createCompany(); + + Repository repo = (Repository)getRepository(); + CDOSession session = openModel1Session(); + + CDOTransaction transaction = session.openTransaction(); + IView view = repo.getSessionManager().getSession(session.getSessionID()).getView(transaction.getViewID()); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoReadLock().lock(); + session.close(); + assertEquals(false, repo.getLockManager().hasLock(RWLockManager.LockType.READ, view, cdoCompany.cdoID())); + } + + public void testAutoReleaseLockFalse_commit() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + transaction.setAutoReleaseLocksEnabled(false); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + cdoCompany.cdoReadLock().lock(); + + msg("Test with read/write lock"); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + company.setCity("Ottawa"); + transaction.commit(); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + msg("Clean locks"); + transaction.unlockObjects(null, null); + + msg("Test with read lock"); + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + company.setCity("Toronto"); + transaction.commit(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + transaction.setAutoReleaseLocksEnabled(true); + transaction.commit(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + } + + public void testAutoReleaseLockFalse_rollback() throws Exception + { + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + transaction.setAutoReleaseLocksEnabled(false); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + cdoCompany.cdoReadLock().lock(); + + msg("Test with read/write lock"); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + company.setCity("Ottawa"); + transaction.rollback(); + assertEquals(true, cdoCompany.cdoWriteLock().isLocked()); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + msg("Clean locks"); + transaction.unlockObjects(null, null); + + msg("Test with read lock"); + cdoCompany.cdoReadLock().lock(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + company.setCity("Toronto"); + transaction.rollback(); + assertEquals(true, cdoCompany.cdoReadLock().isLocked()); + + transaction.setAutoReleaseLocksEnabled(true); + transaction.rollback(); + assertEquals(false, cdoCompany.cdoReadLock().isLocked()); + } + + public void testWriteLockPerformance() throws Exception + { + final int ITERATION = 1000; + Company company = getModel1Factory().createCompany(); + + CDOSession session = openModel1Session(); + CDOTransaction transaction = session.openTransaction(); + CDOResource res = transaction.createResource("/res1"); + res.getContents().add(company); + transaction.commit(); + + long start = System.currentTimeMillis(); + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + + // 335-418 locks/sec + for (int i = 0; i < ITERATION; i++) + { + cdoCompany.cdoWriteLock().lock(); + } + + msg("Lock " + ITERATION / ((double)(System.currentTimeMillis() - start) / 1000) + " objects/sec"); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java index 135a047331..cd65ba6ec9 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java @@ -574,7 +574,7 @@ public class ResourceTest extends AbstractCDOTest assertClean(order, transaction); session.close(); } - + clearCache(getRepository().getRevisionManager()); CDOSession session = openModel1Session(); CDOTransaction transaction = session.openTransaction(); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java index 94374ac611..57d02cefb2 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/StateMachineTest.java @@ -27,6 +27,8 @@ import org.eclipse.emf.internal.cdo.CDOStateMachine; import org.eclipse.emf.internal.cdo.InternalCDOObject; import org.eclipse.emf.internal.cdo.protocol.CommitTransactionResult; +import org.eclipse.net4j.util.io.IOUtil; + import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; @@ -588,7 +590,7 @@ public class StateMachineTest extends AbstractCDOTest private static void assertFailure(IllegalStateException ex) { - ex.printStackTrace(); + IOUtil.print(ex); assertTrue("Expected FAIL transition", ex.getMessage().startsWith("Failing event ")); } @@ -693,7 +695,7 @@ public class StateMachineTest extends AbstractCDOTest } catch (Exception ex) { - ex.printStackTrace(); + IOUtil.print(ex); fail("Reflection problem: " + ex.getMessage()); } finally diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/TransactionTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/TransactionTest.java index bcc10403b6..2ef5f5cc09 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/TransactionTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/TransactionTest.java @@ -16,6 +16,7 @@ import org.eclipse.emf.cdo.CDOTransaction; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.tests.model1.Category; +import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.om.OMPlatform; import java.util.ArrayList; @@ -144,11 +145,11 @@ public class TransactionTest extends AbstractCDOTest boolean timedOut = !latch.await(TIMEOUT, TimeUnit.SECONDS); - for (Exception exp : exceptions) + for (Exception ex : exceptions) { System.out.println(); System.out.println(); - exp.printStackTrace(); + IOUtil.print(ex); } if (timedOut) diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_250757_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_250757_Test.java index eb4d700a08..e045fb149f 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_250757_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_250757_Test.java @@ -19,6 +19,7 @@ import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.model1.PurchaseOrder; import org.eclipse.emf.cdo.tests.model1.Supplier; +import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.transaction.TransactionException; import org.eclipse.emf.ecore.EObject; @@ -123,9 +124,9 @@ public class Bugzilla_250757_Test extends AbstractCDOTest { transaction1.commit(); } - catch (TransactionException e) + catch (TransactionException ex) { - e.printStackTrace(); + IOUtil.print(ex); fail("Should not have an exception"); } } @@ -155,9 +156,9 @@ public class Bugzilla_250757_Test extends AbstractCDOTest { transaction1.commit(); } - catch (TransactionException e) + catch (TransactionException ex) { - e.printStackTrace(); + IOUtil.print(ex); fail("Should not have an exception"); } } diff --git a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java index e204332fad..9df82e344a 100644 --- a/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java +++ b/plugins/org.eclipse.emf.cdo.ui/src/org/eclipse/emf/cdo/internal/ui/editor/CDOEditor.java @@ -1044,7 +1044,7 @@ public class CDOEditor extends MultiPageEditorPart implements IEditingDomainProv } catch (RuntimeException ex) { - ex.printStackTrace(); + OM.LOG.error(ex); throw ex; } } 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 new file mode 100644 index 0000000000..a02c8c95c4 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOLock.java @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.cdo; + +import org.eclipse.net4j.util.concurrent.RWLockManager; + +import java.util.concurrent.locks.Lock; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public interface CDOLock extends Lock +{ + public static final int WAIT = RWLockManager.WAIT; + + public static final int NO_WAIT = RWLockManager.NO_WAIT; + + /** + * Return true if it is currently lock. + */ + public boolean isLocked(); + + public RWLockManager.LockType getType(); +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java index 0f199e8b6e..91eabaa7d9 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOObject.java @@ -39,5 +39,19 @@ public interface CDOObject extends EObject */ public CDOResource cdoDirectResource(); + /** + * Returns read lock associate with this object. + * + * @since 2.0 + */ + public CDOLock cdoReadLock(); + + /** + * Returns write lock associate with this object. + * + * @since 2.0 + */ + public CDOLock cdoWriteLock(); + public void cdoReload(); } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java index dd17d2c8bc..d47cd76ff4 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOTransaction.java @@ -39,6 +39,28 @@ public interface CDOTransaction extends CDOView, CDOUserTransaction public void setCommitTimeout(long timeout); /** + * Specifies whether locks in this view will be removed when {@link CDOTransaction#commit()} or + * {@link CDOTransaction#rollback()} is called. + * <p> + * If false all locks are kept. + * <p> + * Default value is true. + * + * @since 2.0 + */ + public boolean setAutoReleaseLocksEnabled(boolean on); + + /** + * Returns true if locks in this view will be removes when {@link CDOTransaction#commit()} or + * {@link CDOTransaction#rollback()} is called. + * <p> + * Default value is true. + * + * @since 2.0 + */ + public boolean isAutoReleaseLocksEnabled(); + + /** * @since 2.0 */ public long getLastCommitTime(); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java index ae4e013213..5e8514b5dc 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java @@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.eresource.CDOResourceNode; import org.eclipse.emf.cdo.util.ReadOnlyException; import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.event.INotifier; import org.eclipse.emf.common.util.URI; @@ -27,6 +28,7 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.URIHandler; +import java.util.Collection; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -337,5 +339,25 @@ public interface CDOView extends CDOProtocolView, INotifier /** * @since 2.0 */ + public void lockObjects(Collection<? extends CDOObject> objects, RWLockManager.LockType lockType, long timeout) + throws InterruptedException; + + /** + * Unlocks the given locked objects of this view. + * + * @since 2.0 + */ + public void unlockObjects(Collection<? extends CDOObject> objects, RWLockManager.LockType lockType); + + /** + * Unlocks all locked objects of this view. + * + * @since 2.0 + */ + public void unlockObjects(); + + /** + * @since 2.0 + */ public CDOQuery createQuery(String language, String queryString); } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java index bd266242e5..61b7e3a735 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java @@ -11,6 +11,7 @@ **************************************************************************/ package org.eclipse.emf.internal.cdo; +import org.eclipse.emf.cdo.CDOLock; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.CDOView; import org.eclipse.emf.cdo.common.id.CDOID; @@ -29,6 +30,9 @@ import org.eclipse.emf.internal.cdo.util.GenUtil; import org.eclipse.emf.internal.cdo.util.ModelUtil; import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.emf.common.notify.Adapter; @@ -58,9 +62,12 @@ import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.ecore.util.InternalEList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; /** * @author Eike Stepper @@ -78,7 +85,7 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec private InternalCDORevision revision; /** - * CDO used this list instead of eSettings for transient objects. EMF used eSettings as cache. CDO deactivated the + * CDO used this list instead of eSettings for transient objects. EMF used eSettings as cache. CDO deactivates the * cache but EMF still used eSettings to store list wrappers. CDO needs another place to store the real list with the * actual data (transient mode) and accessible through EStore. This allows CDO to always use the same instance of the * list wrapper. @@ -297,6 +304,34 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec cdoSettings = null; } + /** + * @since 2.0 + */ + public CDOLock cdoReadLock() + { + if (FSMUtil.isTransient(this) || FSMUtil.isNew(this)) + { + return NOOPLockImpl.INSTANCE; + } + + // Should we cache the locks ? + return new CDOLockImpl(RWLockManager.LockType.READ); + } + + /** + * @since 2.0 + */ + public CDOLock cdoWriteLock() + { + if (FSMUtil.isTransient(this) || FSMUtil.isNew(this)) + { + return NOOPLockImpl.INSTANCE; + } + + // Should we cache the locks ? + return new CDOLockImpl(RWLockManager.LockType.WRITE); + } + @SuppressWarnings("unchecked") private void populateRevisionFeature(CDOViewImpl view, InternalCDORevision revision, EStructuralFeature eFeature, Object[] eSettings, int i) @@ -976,7 +1011,92 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec } /** - * @author Eike Stepper + * @author Simon McDuff + * @since 2.0 + */ + private final class CDOLockImpl implements CDOLock + { + private RWLockManager.LockType type; + + public CDOLockImpl(RWLockManager.LockType type) + { + this.type = type; + } + + public RWLockManager.LockType getType() + { + return type; + } + + public boolean isLocked() + { + return cdoView().isLocked(CDOObjectImpl.this, type); + } + + public void lock() + { + try + { + cdoView().lockObjects(Collections.singletonList(CDOObjectImpl.this), type, CDOLock.WAIT); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + public void lockInterruptibly() throws InterruptedException + { + lock(); + } + + public Condition newCondition() + { + throw new UnsupportedOperationException(); + } + + public boolean tryLock() + { + try + { + cdoView().lockObjects(Collections.singletonList(CDOObjectImpl.this), type, CDOLock.NO_WAIT); + return true; + } + catch (TimeoutRuntimeException ex) + { + return false; + } + catch (InterruptedException ex) + { + return false; + } + } + + /** + * @throws will + * throw an exception if timeout is reached. + */ + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException + { + try + { + cdoView().lockObjects(Collections.singletonList(CDOObjectImpl.this), type, unit.toMillis(time)); + return true; + } + catch (TimeoutRuntimeException ex) + { + return false; + } + } + + public void unlock() + { + cdoView().unlockObjects(Collections.singletonList(CDOObjectImpl.this), type); + } + } + + /** + * @author Simon McDuff * @since 2.0 */ public static class CDOStoreSettingsImpl implements InternalEObject.EStore diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectWrapper.java index 78441c6506..699451eac6 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectWrapper.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectWrapper.java @@ -11,6 +11,7 @@ package org.eclipse.emf.internal.cdo; import org.eclipse.emf.cdo.CDOView; +import org.eclipse.emf.cdo.CDOLock; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; @@ -125,6 +126,22 @@ public abstract class CDOObjectWrapper implements InternalCDOObject /** * @since 2.0 */ + public CDOLock cdoReadLock() + { + throw new UnsupportedOperationException(); + } + + /** + * @since 2.0 + */ + public CDOLock cdoWriteLock() + { + throw new UnsupportedOperationException(); + } + + /** + * @since 2.0 + */ public void eFireRead(int featureID) { // Do nothing diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java index fdf07f55f7..456e9f9f56 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java @@ -95,6 +95,8 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa private CDOTransactionStrategy transactionStrategy; + private boolean autoReleaseLocksEnabled = true; + public CDOTransactionImpl(int id, CDOSessionImpl session) { super(session, id); @@ -168,6 +170,29 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa /** * @since 2.0 */ + synchronized public boolean setAutoReleaseLocksEnabled(boolean on) + { + try + { + return autoReleaseLocksEnabled; + } + finally + { + autoReleaseLocksEnabled = on; + } + } + + /** + * @since 2.0 + */ + public boolean isAutoReleaseLocksEnabled() + { + return autoReleaseLocksEnabled; + } + + /** + * @since 2.0 + */ public long getLastCommitTime() { return lastCommitTime; @@ -718,6 +743,12 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa // Load from first savepoint up to current savepoint loadSavepoint(lastSavepoint, idsOfNewObjectWithDeltas); + if (lastSavepoint == firstSavepoint && isAutoReleaseLocksEnabled()) + { + // Unlock all objects + unlockObjects(null, null); + } + Map<CDOIDTemp, CDOID> idMappings = Collections.emptyMap(); fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.ROLLED_BACK, idMappings)); for (CDOTransactionHandler handler : getHandlers()) @@ -1158,6 +1189,14 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa throw new TransactionException(ex); } } + else + { + // Removes locks even if no one touch the transaction + if (isAutoReleaseLocksEnabled()) + { + unlockObjects(null, null); + } + } } @SuppressWarnings("unchecked") diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java index aaf101509d..01048a54eb 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java @@ -51,14 +51,20 @@ import org.eclipse.emf.cdo.util.ReadOnlyException; import org.eclipse.emf.internal.cdo.bundle.OM; import org.eclipse.emf.internal.cdo.protocol.ChangeSubscriptionRequest; +import org.eclipse.emf.internal.cdo.protocol.LockObjectsRequest; +import org.eclipse.emf.internal.cdo.protocol.ObjectLockedRequest; +import org.eclipse.emf.internal.cdo.protocol.UnlockObjectsRequest; import org.eclipse.emf.internal.cdo.query.CDOQueryImpl; import org.eclipse.emf.internal.cdo.util.FSMUtil; import org.eclipse.emf.internal.cdo.util.ModelUtil; +import org.eclipse.net4j.signal.SignalRemoteException; import org.eclipse.net4j.util.ImplementationError; import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.concurrent.RWLockManager; import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.net4j.util.ref.ReferenceValueMap; import org.eclipse.net4j.util.transaction.TransactionException; @@ -215,6 +221,89 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement return lock; } + /** + * @throws InterruptedException + * @since 2.0 + */ + public void lockObjects(Collection<? extends CDOObject> objects, RWLockManager.LockType lockType, long timeout) + throws InterruptedException + { + InterruptedException interruptedException = null; + RuntimeException runtimeException = null; + + try + { + LockObjectsRequest request = new LockObjectsRequest(getSession().getProtocol(), this, objects, timeout, lockType); + getSession().getFailOverStrategy().send(request); + } + catch (SignalRemoteException ex) + { + if (ex.getCause() instanceof RuntimeException) + { + runtimeException = (RuntimeException)ex.getCause(); + } + else if (ex.getCause() instanceof InterruptedException) + { + interruptedException = (InterruptedException)ex.getCause(); + } + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + + if (interruptedException != null) + { + throw interruptedException; + } + + if (runtimeException != null) + { + throw runtimeException; + } + } + + /** + * @since 2.0 + */ + public void unlockObjects(Collection<? extends CDOObject> objects, RWLockManager.LockType lockType) + { + try + { + UnlockObjectsRequest request = new UnlockObjectsRequest(getSession().getProtocol(), this, objects, lockType); + getSession().getFailOverStrategy().send(request); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + + /** + * @since 2.0 + */ + public void unlockObjects() + { + unlockObjects(null, null); + } + + /** + * @throws InterruptedException + * @since 2.0 + */ + public boolean isLocked(CDOObject object, RWLockManager.LockType lockType) + { + try + { + ObjectLockedRequest request = new ObjectLockedRequest(getSession().getProtocol(), this, object, lockType); + return getSession().getFailOverStrategy().send(request); + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + } + public boolean isDirty() { return false; diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/NOOPLockImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/NOOPLockImpl.java new file mode 100644 index 0000000000..4debb2dc0a --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/NOOPLockImpl.java @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.internal.cdo; + +import org.eclipse.emf.cdo.CDOLock; + +import org.eclipse.net4j.util.concurrent.RWLockManager; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; + +/** + * @author Simon McDuff + * @since 2.0 + */ +public class NOOPLockImpl implements CDOLock +{ + public static final NOOPLockImpl INSTANCE = new NOOPLockImpl(); + + private NOOPLockImpl() + { + } + + public boolean isLocked() + { + return false; + } + + public void lock() + { + throw new UnsupportedOperationException(); + } + + public void lockInterruptibly() throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public Condition newCondition() + { + return null; + } + + public boolean tryLock() + { + return false; + } + + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException + { + return false; + } + + public void unlock() + { + throw new UnsupportedOperationException(); + } + + public RWLockManager.LockType getType() + { + return null; + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java index 7648b20f9d..45e51ae26e 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/CommitTransactionRequest.java @@ -106,6 +106,7 @@ public class CommitTransactionRequest extends CDOClientRequest<CommitTransaction Collection<CDORevisionDelta> revisionDeltas = commitContext.getRevisionDeltas().values(); Collection<CDOID> detachedObjects = commitContext.getDetachedObjects().keySet(); + out.writeBoolean(commitContext.getTransaction().isAutoReleaseLocksEnabled()); out.writeInt(newPackages.size()); out.writeInt(newResources.size() + newObjects.size()); out.writeInt(revisionDeltas.size()); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/LockObjectsRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/LockObjectsRequest.java new file mode 100644 index 0000000000..a0fde6d651 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/LockObjectsRequest.java @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.internal.cdo.protocol; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOView; +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; + +import org.eclipse.emf.internal.cdo.bundle.OM; + +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Simon McDuff + */ +public class LockObjectsRequest extends CDOClientRequest<Object> +{ + private static final ContextTracer PROTOCOL_TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, LockObjectsRequest.class); + + private CDOView view; + + 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) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS); + this.view = view; + this.objects = objects; + this.timeout = timeout; + this.lockType = lockType; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeInt(view.getViewID()); + out.writeCDOLockType(lockType); + out.writeLong(timeout); + + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Locking of type {0} requested for view {1} with timeout {2}", + lockType == RWLockManager.LockType.READ ? "read" : "write", view.getViewID(), timeout); + } + + out.writeInt(objects.size()); + for (CDOObject object : objects) + { + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Locking requested for objects {0}", object.cdoID()); + } + + out.writeCDOID(object.cdoID()); + } + } + + @Override + protected Object confirming(CDODataInput in) throws IOException + { + in.readBoolean(); + return null; + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ObjectLockedRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ObjectLockedRequest.java new file mode 100644 index 0000000000..7de2be3cf6 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ObjectLockedRequest.java @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.internal.cdo.protocol; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOView; +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; + +import org.eclipse.emf.internal.cdo.bundle.OM; + +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class ObjectLockedRequest extends CDOClientRequest<Boolean> +{ + private static final ContextTracer PROTOCOL_TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, ObjectLockedRequest.class); + + private CDOView view; + + private CDOObject object; + + private RWLockManager.LockType lockType; + + public ObjectLockedRequest(CDOClientProtocol protocol, CDOView view, CDOObject object, RWLockManager.LockType lockType) + { + super(protocol, CDOProtocolConstants.SIGNAL_OBJECT_LOCKED); + this.view = view; + this.object = object; + this.lockType = lockType; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Requesting if object {0} has of lock for object {1}", object.cdoID(), + lockType == RWLockManager.LockType.READ ? "read" : "write"); + } + + out.writeInt(view.getViewID()); + out.writeCDOLockType(lockType); + out.writeCDOID(object.cdoID()); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/UnlockObjectsRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/UnlockObjectsRequest.java new file mode 100644 index 0000000000..294f8f229d --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/UnlockObjectsRequest.java @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.emf.internal.cdo.protocol; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOView; +import org.eclipse.emf.cdo.common.CDODataInput; +import org.eclipse.emf.cdo.common.CDODataOutput; +import org.eclipse.emf.cdo.common.CDOProtocolConstants; + +import org.eclipse.emf.internal.cdo.bundle.OM; + +import org.eclipse.net4j.util.concurrent.RWLockManager; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Simon McDuff + */ +public class UnlockObjectsRequest extends CDOClientRequest<Boolean> +{ + private static final ContextTracer PROTOCOL_TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, UnlockObjectsRequest.class); + + private CDOView view; + + private Collection<? extends CDOObject> objects; + + private RWLockManager.LockType lockType; + + public UnlockObjectsRequest(CDOClientProtocol protocol, CDOView view, Collection<? extends CDOObject> objects, + RWLockManager.LockType lockType) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS); + this.view = view; + this.objects = objects; + this.lockType = lockType; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeInt(view.getViewID()); + out.writeCDOLockType(lockType); + if (objects == null) + { + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Unlocking all objects for view {0}", view.getViewID()); + } + + out.writeInt(CDOProtocolConstants.RELEASE_ALL_LOCKS); + } + else + { + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Unlocking of type {0} requested for view {1}", + lockType == RWLockManager.LockType.READ ? "read" : "write", view.getViewID()); + } + + out.writeInt(objects.size()); + for (CDOObject object : objects) + { + if (PROTOCOL_TRACER.isEnabled()) + { + PROTOCOL_TRACER.format("Unlocking requested for objects {0}", object.cdoID()); + } + + out.writeCDOID(object.cdoID()); + } + } + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/plugins/org.eclipse.net4j.http/src/org/eclipse/net4j/internal/http/HTTPConnectorFactory.java b/plugins/org.eclipse.net4j.http/src/org/eclipse/net4j/internal/http/HTTPConnectorFactory.java index bf27e94845..02a1f5f7f7 100644 --- a/plugins/org.eclipse.net4j.http/src/org/eclipse/net4j/internal/http/HTTPConnectorFactory.java +++ b/plugins/org.eclipse.net4j.http/src/org/eclipse/net4j/internal/http/HTTPConnectorFactory.java @@ -10,6 +10,8 @@ **************************************************************************/ package org.eclipse.net4j.internal.http; +import org.eclipse.net4j.http.internal.common.bundle.OM; + import org.eclipse.spi.net4j.ConnectorFactory; import java.net.MalformedURLException; @@ -44,7 +46,7 @@ public class HTTPConnectorFactory extends ConnectorFactory } catch (MalformedURLException ex) { - ex.printStackTrace(); + OM.LOG.error(ex); } HTTPClientConnector connector = new HTTPClientConnector(); diff --git a/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSSeparatedTest.java b/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSSeparatedTest.java index 15f2df9513..9699d8f8fb 100644 --- a/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSSeparatedTest.java +++ b/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSSeparatedTest.java @@ -140,7 +140,7 @@ public class JMSSeparatedTest } catch (JMSException ex) { - ex.printStackTrace(); + IOUtil.print(ex); } } } diff --git a/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSTest.java b/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSTest.java index 819fee4e74..918a5cf944 100644 --- a/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSTest.java +++ b/plugins/org.eclipse.net4j.jms.tests/src/org/eclipse/net4j/jms/tests/JMSTest.java @@ -125,7 +125,7 @@ public class JMSTest } catch (JMSException ex) { - ex.printStackTrace(); + IOUtil.print(ex); } } } diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/ChannelTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/ChannelTest.java index c83e7ea536..4b7b75dc43 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/ChannelTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/ChannelTest.java @@ -107,7 +107,7 @@ public abstract class ChannelTest extends AbstractProtocolTest byte[] data = TinyData.getBytes(); byte[] result = new ArrayRequest(protocol, data).send(); - assertTrue(Arrays.equals(data, result)); + assertEquals(true, Arrays.equals(data, result)); protocol.close(); assertInactive(protocol); @@ -172,7 +172,7 @@ public abstract class ChannelTest extends AbstractProtocolTest byte[] data = TinyData.getBytes(); byte[] result = new ArrayRequest(protocol, data).send(); - assertTrue(Arrays.equals(data, result)); + assertEquals(true, Arrays.equals(data, result)); heartBeat(); protocol.close(); @@ -208,7 +208,7 @@ public abstract class ChannelTest extends AbstractProtocolTest { byte[] data = TinyData.getBytes(); byte[] result = new ArrayRequest(protocol, data).send(); - assertTrue(Arrays.equals(data, result)); + assertEquals(true, Arrays.equals(data, result)); heartBeat(); ConcurrencyUtil.sleep(10L); diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/Performance.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/Performance.java index 6b3a4572b4..184827da56 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/Performance.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/Performance.java @@ -1,5 +1,7 @@ package org.eclipse.net4j.tests; +import org.eclipse.net4j.util.io.IOUtil; + import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -95,7 +97,7 @@ public class Performance } catch (IOException ex) { - ex.printStackTrace(); + IOUtil.print(ex); latch.countDown(); } } diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/SignalTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/SignalTest.java index 6b2a36bf53..9607845ac6 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/SignalTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/SignalTest.java @@ -60,7 +60,7 @@ public class SignalTest extends AbstractProtocolTest protocol = new TestSignalProtocol(connector); byte[] data = TinyData.getBytes(); byte[] result = new ArrayRequest(protocol, data).send(); - assertTrue(Arrays.equals(data, result)); + assertEquals(true, Arrays.equals(data, result)); } finally { diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TCPConnectorTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TCPConnectorTest.java index 4a7cba1bb3..1485350e34 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TCPConnectorTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TCPConnectorTest.java @@ -298,7 +298,7 @@ public class TCPConnectorTest extends AbstractOMTest catch (ConnectorException ex) { OM.LOG.info("Expected ConnectorException:", ex); - assertTrue(ex.getCause() instanceof NegotiationException); + assertEquals(true, ex.getCause() instanceof NegotiationException); } } @@ -360,7 +360,7 @@ public class TCPConnectorTest extends AbstractOMTest catch (ConnectorException ex) { OM.LOG.info("Expected ConnectorException:", ex); - assertTrue(ex.getCause() instanceof NegotiationException); + assertEquals(true, ex.getCause() instanceof NegotiationException); } } } diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TransportTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TransportTest.java index 6e22477f4b..24e4a308e4 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TransportTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/TransportTest.java @@ -21,6 +21,7 @@ import org.eclipse.net4j.util.container.IContainerDelta; import org.eclipse.net4j.util.container.IContainerEvent; import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; +import org.eclipse.net4j.util.io.IOUtil; import java.io.BufferedReader; import java.io.DataInputStream; @@ -88,7 +89,7 @@ public abstract class TransportTest extends AbstractProtocolTest sleep(50); } - assertTrue(counter.await(2, TimeUnit.SECONDS)); + assertEquals(true, counter.await(2, TimeUnit.SECONDS)); } public void testStreaming() throws Exception @@ -124,7 +125,7 @@ public abstract class TransportTest extends AbstractProtocolTest }); IChannel channel = getConnector().openChannel(); - assertTrue(counter.await(2, TimeUnit.SECONDS)); + assertEquals(true, counter.await(2, TimeUnit.SECONDS)); assertNotNull(inputStream[0]); ChannelOutputStream outputStream = new ChannelOutputStream(channel); @@ -146,7 +147,7 @@ public abstract class TransportTest extends AbstractProtocolTest } catch (RuntimeException ex) { - ex.printStackTrace(); + IOUtil.print(ex); } } @@ -186,7 +187,7 @@ public abstract class TransportTest extends AbstractProtocolTest }); IChannel channel = getConnector().openChannel(); - assertTrue(counter.await(2, TimeUnit.SECONDS)); + assertEquals(true, counter.await(2, TimeUnit.SECONDS)); assertNotNull(inputStream[0]); ChannelOutputStream outputStream = new ChannelOutputStream(channel); @@ -215,7 +216,7 @@ public abstract class TransportTest extends AbstractProtocolTest } catch (RuntimeException ex) { - ex.printStackTrace(); + IOUtil.print(ex); } } @@ -252,7 +253,7 @@ public abstract class TransportTest extends AbstractProtocolTest }); final IChannel channel = getConnector().openChannel(); - assertTrue(counter.await(2, TimeUnit.SECONDS)); + assertEquals(true, counter.await(2, TimeUnit.SECONDS)); assertNotNull(inputStream[0]); new Thread() @@ -277,7 +278,7 @@ public abstract class TransportTest extends AbstractProtocolTest } catch (IOException ex) { - ex.printStackTrace(); + IOUtil.print(ex); fail(ex.getLocalizedMessage()); } } @@ -297,7 +298,7 @@ public abstract class TransportTest extends AbstractProtocolTest } catch (RuntimeException ex) { - ex.printStackTrace(); + IOUtil.print(ex); } } @@ -334,7 +335,7 @@ public abstract class TransportTest extends AbstractProtocolTest }); IChannel channel = getConnector().openChannel(); - assertTrue(counter.await(2, TimeUnit.SECONDS)); + assertEquals(true, counter.await(2, TimeUnit.SECONDS)); ChannelOutputStream outputStream = new ChannelOutputStream(channel); DataOutputStream dataOutput = new DataOutputStream(outputStream); diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/bugzilla/Bugzilla241463_Test.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/bugzilla/Bugzilla241463_Test.java index 63d1080bbf..ac71a86745 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/bugzilla/Bugzilla241463_Test.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/tests/bugzilla/Bugzilla241463_Test.java @@ -26,6 +26,7 @@ import org.eclipse.net4j.util.ImplementationError; import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; import org.eclipse.net4j.util.container.IManagedContainer; import org.eclipse.net4j.util.container.ManagedContainer; +import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.security.RandomizerFactory; import org.eclipse.internal.net4j.ExecutorServiceFactory; @@ -76,13 +77,16 @@ public class Bugzilla241463_Test extends AbstractTransportTest { fail("TimeoutRuntimeException expected"); } + + // Success } catch (TimeoutRuntimeException expected) { + // Success } catch (Throwable wrongException) { - wrongException.printStackTrace(); + IOUtil.print(wrongException); fail("TimeoutRuntimeException expected"); } } diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ExtendedIOTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ExtendedIOTest.java index f48fad416c..2f303a8c47 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ExtendedIOTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ExtendedIOTest.java @@ -50,7 +50,7 @@ public class ExtendedIOTest extends AbstractOMTest ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ExtendedDataInputStream edis = new ExtendedDataInputStream(bais); byte[] result = (byte[])edis.readObject(); - assertTrue(Arrays.equals(byteArray, result)); + assertEquals(true, Arrays.equals(byteArray, result)); } public void testObject2() throws Exception @@ -64,7 +64,7 @@ public class ExtendedIOTest extends AbstractOMTest ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ExtendedDataInputStream edis = new ExtendedDataInputStream(bais); byte[] result = (byte[])edis.readObject(); - assertTrue(Arrays.equals(byteArray, result)); + assertEquals(true, Arrays.equals(byteArray, result)); } public void testByteArray1() throws Exception @@ -78,7 +78,7 @@ public class ExtendedIOTest extends AbstractOMTest ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ExtendedDataInputStream edis = new ExtendedDataInputStream(bais); byte[] result = edis.readByteArray(); - assertTrue(Arrays.equals(byteArray, result)); + assertEquals(true, Arrays.equals(byteArray, result)); } public void testByteArray2() throws Exception @@ -92,7 +92,7 @@ public class ExtendedIOTest extends AbstractOMTest ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ExtendedDataInputStream edis = new ExtendedDataInputStream(bais); byte[] result = edis.readByteArray(); - assertTrue(Arrays.equals(byteArray, result)); + assertEquals(true, Arrays.equals(byteArray, result)); } private byte[] createByteArray1() throws IOException diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ReferenceValueMapTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ReferenceValueMapTest.java index 9dbb7419ad..3c13f556a0 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ReferenceValueMapTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/ReferenceValueMapTest.java @@ -25,7 +25,7 @@ public class ReferenceValueMapTest extends AbstractOMTest map.put("SIMON", new Object()); System.gc(); map.put("SIMON", new Object()); - assertTrue(map.size() >= 0); + assertEquals(true, map.size() >= 0); } } @@ -37,7 +37,7 @@ public class ReferenceValueMapTest extends AbstractOMTest map.put("SIMON", new Object()); System.gc(); map.put("SIMON2", new Object()); - assertTrue(map.size() >= 1); + assertEquals(true, map.size() >= 1); } } } diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/SynchronizingCorrelatorTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/SynchronizingCorrelatorTest.java index 47054dcec6..de85c293ae 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/SynchronizingCorrelatorTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/SynchronizingCorrelatorTest.java @@ -69,7 +69,7 @@ public class SynchronizingCorrelatorTest extends AbstractOMTest boolean consumed = correlator.put("eike", true, 1000); msg("Consumed: " + consumed); - assertTrue(consumed); + assertEquals(true, consumed); consumer.join(1000); assertEquals(Boolean.TRUE, result[0]); @@ -121,7 +121,7 @@ public class SynchronizingCorrelatorTest extends AbstractOMTest final SynchronizingCorrelator<String, Boolean> correlator = new SynchronizingCorrelator<String, Boolean>(); boolean consumed = correlator.put("eike", true, 50); msg("Consumed: " + consumed); - assertFalse(consumed); + assertEquals(false, consumed); final Thread consumer = new Thread() { diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/UTFTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/UTFTest.java index fd0375e551..507f2ce354 100644 --- a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/UTFTest.java +++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/UTFTest.java @@ -33,7 +33,7 @@ public class UTFTest extends AbstractOMTest } String str = builder.toString(); - assertTrue(str.length() > UNSIGNED_SHORT_MAX); + assertEquals(true, str.length() > UNSIGNED_SHORT_MAX); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); 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 new file mode 100644 index 0000000000..5dbe470f80 --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWLockManager.java @@ -0,0 +1,567 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 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 + **************************************************************************/ +package org.eclipse.net4j.util.concurrent; + +import org.eclipse.net4j.util.collection.HashBag; +import org.eclipse.net4j.util.lifecycle.Lifecycle; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Support Multiple reads/no write and upgrade lock from read to write. Many context could request + * {@link LockType#WRITE write} lock at the same time. It will privileges first context that has already a + * {@link LockType#READ read} lock. If no one has any read lock, it's "first come first serve". + * + * @author Simon McDuff + * @since 2.0 + */ +public class RWLockManager<K, V> extends Lifecycle +{ + public static final int WAIT = 0; + + public static final int NO_WAIT = 1; + + private LockStrategy<K, V> writeLockStrategy = new LockStrategy<K, V>() + { + public boolean canObtainLock(LockEntry<K, V> entry, V context) + { + return entry.canObtainWriteLock(context); + } + + public LockEntry<K, V> lock(LockEntry<K, V> entry, V context) + { + return entry.writeLock(context); + } + + public LockEntry<K, V> unlock(LockEntry<K, V> entry, V context) + { + return entry.writeUnlock(context); + } + + public boolean isLocked(LockEntry<K, V> entry, V context) + { + return entry.isWriteLock(context); + } + }; + + private LockStrategy<K, V> readLockStrategy = new LockStrategy<K, V>() + { + public boolean canObtainLock(LockEntry<K, V> entry, V context) + { + return entry.canObtainReadLock(context); + } + + public LockEntry<K, V> lock(LockEntry<K, V> entry, V context) + { + return entry.readLock(context); + } + + public LockEntry<K, V> unlock(LockEntry<K, V> entry, V context) + { + return entry.readUnlock(context); + } + + public boolean isLocked(LockEntry<K, V> entry, V context) + { + return entry.isReadLock(context); + } + }; + + private Map<K, LockEntry<K, V>> lockEntries = new HashMap<K, LockEntry<K, V>>(); + + private Object lockChanged = new Object(); + + public void lock(RWLockManager.LockType type, V context, Collection<K> objectsToLock, long timeout) + throws InterruptedException + { + lock(getLockingStrategy(type), context, objectsToLock, timeout); + } + + public void lock(RWLockManager.LockType type, V context, K objectToLock, long timeout) throws InterruptedException + { + lock(type, context, Collections.singletonList(objectToLock), timeout); + } + + /** + * 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) + { + unlock(getLockingStrategy(type), context, objectToLock); + } + + /** + * Attempts to release all locks(read and write) for a given context. + */ + public void unlock(V context) + { + synchronized (lockChanged) + { + List<LockEntry<K, V>> lockEntrysToRemove = new ArrayList<LockEntry<K, V>>(); + List<LockEntry<K, V>> lockEntrysToAdd = new ArrayList<LockEntry<K, V>>(); + + for (Entry<K, LockEntry<K, V>> entry : lockEntries.entrySet()) + { + LockEntry<K, V> newEntry = entry.getValue().clearLock(context); + if (newEntry == null) + { + lockEntrysToRemove.add(entry.getValue()); + } + else if (newEntry != entry) + { + lockEntrysToAdd.add(newEntry); + } + } + + for (LockEntry<K, V> lockEntry : lockEntrysToRemove) + { + lockEntries.remove(lockEntry.getKey()); + } + + for (LockEntry<K, V> lockEntry : lockEntrysToAdd) + { + lockEntries.put(lockEntry.getKey(), lockEntry); + } + + lockChanged.notifyAll(); + } + } + + public boolean hasLock(RWLockManager.LockType type, V context, K objectToLock) + { + return hasLock(getLockingStrategy(type), context, objectToLock); + } + + private LockStrategy<K, V> getLockingStrategy(RWLockManager.LockType type) + { + if (type == RWLockManager.LockType.READ) + { + return readLockStrategy; + } + + if (type == RWLockManager.LockType.WRITE) + { + return writeLockStrategy; + } + + throw new IllegalArgumentException(type.toString()); + } + + /** + * Attempts to release this lock. + * <p> + * If the number of context is now zero then the lock is made available for write lock attempts. + * + * @throws IllegalMonitorStateException + * Unlocking object not locked. + */ + private void unlock(LockStrategy<K, V> lockingStrategy, V context, Collection<K> objectsToLock) + { + synchronized (lockChanged) + { + List<LockEntry<K, V>> lockEntrysToRemove = new ArrayList<LockEntry<K, V>>(); + List<LockEntry<K, V>> lockEntrysToAdd = new ArrayList<LockEntry<K, V>>(); + for (K objectToLock : objectsToLock) + { + LockEntry<K, V> entry = lockEntries.get(objectToLock); + + if (entry == null) + { + throw new IllegalMonitorStateException(); + } + + LockEntry<K, V> newEntry = lockingStrategy.unlock(entry, context); + + if (newEntry == null) + { + lockEntrysToRemove.add(entry); + } + else if (newEntry != entry) + { + lockEntrysToAdd.add(newEntry); + } + } + + for (LockEntry<K, V> lockEntry : lockEntrysToRemove) + { + lockEntries.remove(lockEntry.getKey()); + } + + for (LockEntry<K, V> lockEntry : lockEntrysToAdd) + { + lockEntries.put(lockEntry.getKey(), lockEntry); + } + + lockChanged.notifyAll(); + } + } + + private boolean hasLock(LockStrategy<K, V> lockingStrategy, V context, K objectToLock) + { + LockEntry<K, V> entry = getLockEntry(objectToLock); + return entry != null && lockingStrategy.isLocked(entry, context); + } + + private void lock(LockStrategy<K, V> lockStrategy, V context, Collection<K> objectToLocks, long timeout) + throws InterruptedException + { + long startTime = System.currentTimeMillis(); + while (true) + { + synchronized (lockChanged) + { + K conflict = obtainLock(lockStrategy, context, objectToLocks); + if (conflict == null) + { + lockChanged.notifyAll(); + return; + } + + long elapsedTime = System.currentTimeMillis() - startTime; + if (timeout != WAIT && elapsedTime > timeout) + { + throw new TimeoutRuntimeException("Conflict with " + conflict); + } + + if (timeout == WAIT) + { + lockChanged.wait(); + } + else + { + lockChanged.wait(Math.max(1, timeout - elapsedTime)); + } + } + } + } + + private K obtainLock(LockStrategy<K, V> lockingStrategy, V context, Collection<K> objectsToLock) + { + List<LockEntry<K, V>> lockEntrys = new ArrayList<LockEntry<K, V>>(); + for (K objectToLock : objectsToLock) + { + LockEntry<K, V> entry = lockEntries.get(objectToLock); + if (entry == null) + { + entry = new NoLockEntry<K, V>(objectToLock); + } + + if (lockingStrategy.canObtainLock(entry, context)) + { + lockEntrys.add(entry); + } + else + { + return objectToLock; + } + } + + for (LockEntry<K, V> lockEntry : lockEntrys) + { + lockEntries.put(lockEntry.getKey(), lockingStrategy.lock(lockEntry, context)); + } + + return null; + } + + private LockEntry<K, V> getLockEntry(K objectToLock) + { + synchronized (lockChanged) + { + return lockEntries.get(objectToLock); + } + } + + /** + * @author Simon McDuff + */ + private interface LockStrategy<K, V> + { + public boolean isLocked(LockEntry<K, V> entry, V context); + + public boolean canObtainLock(LockEntry<K, V> entry, V context); + + public LockEntry<K, V> lock(LockEntry<K, V> entry, V context); + + public LockEntry<K, V> unlock(LockEntry<K, V> entry, V context); + } + + /** + * @author Simon McDuff + */ + private interface LockEntry<K, V> + { + public K getKey(); + + public boolean isReadLock(V context); + + public boolean isWriteLock(V context); + + public boolean canObtainReadLock(V context); + + public boolean canObtainWriteLock(V context); + + public LockEntry<K, V> readLock(V context); + + public LockEntry<K, V> writeLock(V context); + + public LockEntry<K, V> readUnlock(V context); + + public LockEntry<K, V> writeUnlock(V context); + + public LockEntry<K, V> clearLock(V context); + } + + /** + * @author Simon McDuff + */ + private static final class ReadLockEntry<K, V> implements LockEntry<K, V> + { + private K id; + + private Set<V> contexts = new HashBag<V>(); + + public ReadLockEntry(K objectToLock, V context) + { + this.id = objectToLock; + contexts.add(context); + } + + public boolean canObtainReadLock(V context) + { + return true; + } + + public boolean canObtainWriteLock(V context) + { + return contexts.size() == 1 && contexts.contains(context); + } + + public LockEntry<K, V> readLock(V context) + { + contexts.add(context); + return this; + } + + public LockEntry<K, V> writeLock(V context) + { + return new WriteLockEntry<K, V>(id, context, this); + } + + public K getKey() + { + return id; + } + + public LockEntry<K, V> readUnlock(V context) + { + contexts.remove(context); + return contexts.isEmpty() ? null : this; + } + + public LockEntry<K, V> writeUnlock(V context) + { + throw new IllegalMonitorStateException(); + } + + public boolean isReadLock(V context) + { + return contexts.contains(context); + } + + public boolean isWriteLock(V context) + { + return false; + } + + public LockEntry<K, V> clearLock(V context) + { + return null; + } + } + + /** + * @author Simon McDuff + */ + private static final class WriteLockEntry<K, V> implements LockEntry<K, V> + { + private K objectToLock; + + private V context; + + private int count; + + private ReadLockEntry<K, V> readLock; + + public WriteLockEntry(K objectToLock, V context, ReadLockEntry<K, V> readLock) + { + this.objectToLock = objectToLock; + this.context = context; + this.readLock = readLock; + this.count = 1; + } + + private ReadLockEntry<K, V> getReadLock() + { + if (readLock == null) + { + readLock = new ReadLockEntry<K, V>(objectToLock, context); + } + + return readLock; + } + + public boolean canObtainWriteLock(V context) + { + return context == this.context; + } + + public boolean canObtainReadLock(V context) + { + return context == this.context; + } + + public LockEntry<K, V> readLock(V context) + { + getReadLock().readLock(context); + return this; + } + + public LockEntry<K, V> writeLock(V context) + { + count++; + return this; + } + + public K getKey() + { + return objectToLock; + } + + public LockEntry<K, V> readUnlock(V context) + { + if (readLock != null) + { + if (getReadLock().readUnlock(context) == null) + { + readLock = null; + } + + return this; + } + + throw new IllegalMonitorStateException(); + } + + public LockEntry<K, V> writeUnlock(V context) + { + return --count <= 0 ? readLock : this; + } + + public boolean isReadLock(V context) + { + return readLock != null ? readLock.isReadLock(context) : false; + } + + public boolean isWriteLock(V context) + { + return context == this.context; + } + + public LockEntry<K, V> clearLock(V context) + { + return null; + } + } + + /** + * @author Simon McDuff + */ + private static final class NoLockEntry<K, V> implements LockEntry<K, V> + { + private K objectToLock; + + public NoLockEntry(K objectToLock) + { + this.objectToLock = objectToLock; + } + + public boolean canObtainWriteLock(V context) + { + return true; + } + + public boolean canObtainReadLock(V context) + { + return true; + } + + public LockEntry<K, V> readLock(V context) + { + return new ReadLockEntry<K, V>(objectToLock, context); + } + + public LockEntry<K, V> writeLock(V context) + { + return new WriteLockEntry<K, V>(objectToLock, context, null); + } + + public K getKey() + { + return objectToLock; + } + + public LockEntry<K, V> readUnlock(V context) + { + throw new UnsupportedOperationException(); + } + + public LockEntry<K, V> writeUnlock(V context) + { + throw new UnsupportedOperationException(); + } + + public boolean isReadLock(V context) + { + throw new UnsupportedOperationException(); + } + + public boolean isWriteLock(V context) + { + throw new UnsupportedOperationException(); + } + + public LockEntry<K, V> clearLock(V context) + { + throw new UnsupportedOperationException(); + } + } + + /** + * @author Simon McDuff + */ + public static enum LockType + { + WRITE, READ + } +} |