diff options
82 files changed, 3588 insertions, 289 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.cdo.common/META-INF/MANIFEST.MF index dcc75cdc06..0b80784f1d 100644 --- a/plugins/org.eclipse.emf.cdo.common/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.cdo.common/META-INF/MANIFEST.MF @@ -56,6 +56,7 @@ Export-Package: org.eclipse.emf.cdo.common;version="4.1.0", org.eclipse.emf.cdo.server.net4j, org.eclipse.emf.cdo.ui, org.eclipse.emf.cdo.tests", + org.eclipse.emf.cdo.internal.common.lock;version="4.1.0";x-internal:=true, org.eclipse.emf.cdo.internal.common.messages;version="4.1.0";x-internal:=true, org.eclipse.emf.cdo.internal.common.model;version="4.1.0"; x-friends:="org.eclipse.emf.cdo.common, @@ -97,5 +98,6 @@ Export-Package: org.eclipse.emf.cdo.common;version="4.1.0", org.eclipse.emf.cdo.spi.common.branch;version="4.1.0", org.eclipse.emf.cdo.spi.common.commit;version="4.1.0", org.eclipse.emf.cdo.spi.common.id;version="4.1.0", + org.eclipse.emf.cdo.spi.common.lock;version="4.1.0", org.eclipse.emf.cdo.spi.common.model;version="4.1.0", org.eclipse.emf.cdo.spi.common.revision;version="4.1.0" diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockChangeInfo.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockChangeInfo.java new file mode 100644 index 0000000000..0307719d42 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockChangeInfo.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.common.lock; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; + +/** + * Represents a change in the lock state of a set of objects. Instances are meant to be sent from the server to the + * client for the purpose of notifying the latter. + * + * @author Caspar De Groot + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface CDOLockChangeInfo extends CDOBranchPoint +{ + /** + * @return The branch at which the lock changes took place, same as <code>getView().getBranch()</code>. + */ + public CDOBranch getBranch(); + + /** + * @return The repository time at which the lock changes took place. This is only an informal indication; no formal + * relation (e.g. an ordering) with commit timestamps is guaranteed. + */ + public long getTimeStamp(); + + /** + * @return The view, represented as a {@link CDOLockOwner}, that authored the lock changes. + */ + public CDOLockOwner getLockOwner(); + + /** + * @return The new lock states of the objects that were affected by the change + */ + public CDOLockState[] getLockStates(); + + /** + * @return the type of lock operation that caused the lock changes + */ + public Operation getOperation(); + + /** + * Enumerates the possible locking operations. + * + * @author Caspar De Groot + */ + public enum Operation + { + LOCK, UNLOCK + } +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockOwner.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockOwner.java new file mode 100644 index 0000000000..012fc15117 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockOwner.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.common.lock; + +/** + * A client-side representation of a view owning locks. + * <p> + * + * @author Caspar De Groot + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface CDOLockOwner +{ + /** + * @return the ID identifying the session that owns the view + */ + public int getSessionID(); + + /** + * @return the ID identifying the view within the session + */ + public int getViewID(); + + /** + * A constant to represent on the client-side that a lock's owner cannot be represented as a viewID-sessionID pair. + */ + public static final CDOLockOwner UNKNOWN = new CDOLockOwner() + { + public int getViewID() + { + return 0; + } + + public int getSessionID() + { + return 0; + } + + @Override + public String toString() + { + return CDOLockOwner.class.getSimpleName() + ".UNKNOWN"; + } + }; +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockState.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockState.java new file mode 100644 index 0000000000..0e470b28c3 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockState.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.common.lock; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.util.Set; + +/** + * A client-side representation of <i>all</i> the locks on a single CDOObject. + * <p> + * As an individual lock is always owned by view, which in turn is owned by a session, the methods on this interface + * return instances of {@link CDOLockOwner} which carry that information. + * <p> + * + * @author Caspar De Groot + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface CDOLockState +{ + /** + * Gets a unique identifier for the object that is locked; typically a {@link CDOID} or a {@link CDOIDAndBranch}, + * depending on whether branching support is enabled or not + * + * @return the identifier + */ + public Object getLockedObject(); + + /** + * If the 'others' argument is <code>false</code>, this method returns <code>true</code> if this lock is currently + * held by the <i>requesting</i> CDOView, <code>false</code> otherwise. + * <p> + * If the 'others' argument is <code>true</code>, this method returns <code>true</code> if this lock is currently held + * by <i>another</i> view (i.e. any view different from the requesting one), <code>false</code> otherwise. + */ + public boolean isLocked(LockType lockType, CDOLockOwner lockOwner, boolean others); + + public Set<CDOLockOwner> getReadLockOwners(); + + public CDOLockOwner getWriteLockOwner(); + + public CDOLockOwner getWriteOptionOwner(); +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockUtil.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockUtil.java new file mode 100644 index 0000000000..d36d9078b6 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/CDOLockUtil.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.common.lock; + +import org.eclipse.emf.cdo.common.CDOCommonSession; +import org.eclipse.emf.cdo.common.CDOCommonView; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockChangeInfoImpl; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockOwnerImpl; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockStateImpl; +import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState; + +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; + +/** + * Various static methods that may help with classes related to CDO locks. + * + * @author Caspar De Groot + * @since 4.1 + */ +public final class CDOLockUtil +{ + private CDOLockUtil() + { + } + + public static CDOLockState createLockState(Object target) + { + return new CDOLockStateImpl(target); + } + + public static CDOLockState createLockState(LockState<Object, ? extends CDOCommonView> lockState) + { + CheckUtil.checkArg(lockState, "lockState"); + + InternalCDOLockState cdoLockState = new CDOLockStateImpl(lockState.getLockedObject()); + + for (CDOCommonView view : lockState.getReadLockOwners()) + { + int sessionID = view.getSession().getSessionID(); + int viewID = view.getViewID(); + CDOLockOwner owner = new CDOLockOwnerImpl(sessionID, viewID); + cdoLockState.addReadLockOwner(owner); + } + + CDOCommonView writeLockOwner = lockState.getWriteLockOwner(); + if (writeLockOwner != null) + { + CDOLockOwner owner = createLockOwner(writeLockOwner); + cdoLockState.setWriteLockOwner(owner); + } + + CDOCommonView writeOptionOwner = lockState.getWriteOptionOwner(); + if (writeOptionOwner != null) + { + CDOLockOwner owner = createLockOwner(writeOptionOwner); + cdoLockState.setWriteOptionOwner(owner); + } + + return cdoLockState; + } + + public static CDOLockOwner createLockOwner(CDOCommonView view) + { + CDOCommonSession session = view.getSession(); + if (session != null) + { + int sessionID = session.getSessionID(); + int viewID = view.getViewID(); + return new CDOLockOwnerImpl(sessionID, viewID); + } + return CDOLockOwner.UNKNOWN; + } + + public static CDOLockChangeInfo createLockChangeInfo(long timestamp, CDOLockOwner lockOwner, CDOBranch branch, + Operation op, CDOLockState[] cdoLockStates) + { + return new CDOLockChangeInfoImpl(branch.getPoint(timestamp), lockOwner, cdoLockStates, op); + } + + public static CDOLockChangeInfo createLockChangeInfo(long timestamp, CDOCommonView view, CDOBranch viewedBranch, + Operation op, CDOLockState[] cdoLockStates) + { + CDOLockOwner lockOwner = createLockOwner(view); + return createLockChangeInfo(timestamp, lockOwner, viewedBranch, op, cdoLockStates); + } +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/IDurableLockingManager.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/IDurableLockingManager.java index 7d042af590..5343f8f91a 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/IDurableLockingManager.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/lock/IDurableLockingManager.java @@ -24,8 +24,6 @@ import java.util.Map; *
* @author Eike Stepper
* @since 4.0
- * @noextend This interface is not intended to be extended by clients.
- * @noimplement This interface is not intended to be implemented by clients.
*/
public interface IDurableLockingManager
{
@@ -104,6 +102,28 @@ public interface IDurableLockingManager }
/**
+ * @author Caspar De Groot
+ * @since 4.1
+ */
+ public static class LockAreaAlreadyExistsException extends IllegalStateException
+ {
+ private static final long serialVersionUID = 1L;
+
+ private String durableLockingID;
+
+ public LockAreaAlreadyExistsException(String durableLockingID)
+ {
+ super("A lock area with ID=" + durableLockingID + " already exists");
+ this.durableLockingID = durableLockingID;
+ }
+
+ public String getDurableLockingID()
+ {
+ return durableLockingID;
+ }
+ }
+
+ /**
* Enumerates the possible combinations of read and write locks on a single CDO object.
*
* @author Eike Stepper
@@ -165,7 +185,8 @@ public interface IDurableLockingManager public LockGrade getUpdated(LockType type, boolean on)
{
- int mask = type == LockType.READ ? 1 : 2;
+ int mask = getMask(type);
+
if (on)
{
return get(value | mask);
@@ -174,6 +195,23 @@ public interface IDurableLockingManager return get(value & ~mask);
}
+ private int getMask(LockType type)
+ {
+ switch (type)
+ {
+ case READ:
+ return 1;
+
+ case WRITE:
+ return 2;
+
+ case OPTION:
+ return 4;
+ }
+
+ return 0;
+ }
+
public static LockGrade get(LockType type)
{
if (type == LockType.READ)
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataInput.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataInput.java index 2cbbde9d83..acab6ac3ea 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataInput.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataInput.java @@ -19,6 +19,9 @@ import org.eclipse.emf.cdo.common.commit.CDOCommitData; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.model.CDOPackageInfo; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; @@ -135,4 +138,19 @@ public interface CDODataInput extends ExtendedDataInput // ///////////////////////////////////////////////////////////////////////////////////////////////// public LockType readCDOLockType() throws IOException; + + /** + * @since 4.1 + */ + public CDOLockChangeInfo readCDOLockChangeInfo() throws IOException; + + /** + * @since 4.1 + */ + public CDOLockOwner readCDOLockOwner() throws IOException; + + /** + * @since 4.1 + */ + public CDOLockState readCDOLockState() throws IOException; } diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataOutput.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataOutput.java index db036d7926..9c040d46fe 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataOutput.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDODataOutput.java @@ -20,6 +20,9 @@ import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.model.CDOPackageInfo; import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; @@ -141,4 +144,19 @@ public interface CDODataOutput extends ExtendedDataOutput // ///////////////////////////////////////////////////////////////////////////////////////////////// public void writeCDOLockType(LockType lockType) throws IOException; + + /** + * @since 4.1 + */ + public void writeCDOLockChangeInfo(CDOLockChangeInfo lockChangeInfo) throws IOException; + + /** + * @since 4.1 + */ + public void writeCDOLockState(CDOLockState lockState) throws IOException; + + /** + * @since 4.1 + */ + public void writeCDOLockOwner(CDOLockOwner lockOwner) throws IOException; } diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java index ef2fbfee26..722b158ca9 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java @@ -217,6 +217,31 @@ public interface CDOProtocolConstants */ public static final short SIGNAL_HANDLE_REVISIONS = 48; + /** + * @since 4.1 + */ + public static final short SIGNAL_LOCK_DELEGATION = 49; + + /** + * @since 4.1 + */ + public static final short SIGNAL_UNLOCK_DELEGATION = 50; + + /** + * @since 4.1 + */ + public static final short SIGNAL_LOCK_NOTIFICATION = 51; + + /** + * @since 4.1 + */ + public static final short SIGNAL_LOCK_STATE = 52; + + /** + * @since 4.1 + */ + public static final short SIGNAL_ENABLE_LOCK_NOTIFICATION = 53; + // ////////////////////////////////////////////////////////////////////// // Session Refresh diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockChangeInfoImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockChangeInfoImpl.java new file mode 100644 index 0000000000..70c4c0a65f --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockChangeInfoImpl.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.common.lock; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; + +/** + * @author Caspar De Groot + */ +public class CDOLockChangeInfoImpl implements CDOLockChangeInfo +{ + private final CDOBranchPoint branchPoint; + + private final CDOLockOwner lockOwner; + + private final CDOLockState[] lockStates; + + private final Operation operation; + + public CDOLockChangeInfoImpl(CDOBranchPoint branchPoint, CDOLockOwner lockOwner, CDOLockState[] lockStates, + Operation operation) + { + this.branchPoint = branchPoint; + this.lockOwner = lockOwner; + this.lockStates = lockStates; + this.operation = operation; + } + + public CDOBranch getBranch() + { + return branchPoint.getBranch(); + } + + public long getTimeStamp() + { + return branchPoint.getTimeStamp(); + } + + public CDOLockOwner getLockOwner() + { + return lockOwner; + } + + public CDOLockState[] getLockStates() + { + return lockStates; + } + + public Operation getOperation() + { + return operation; + } +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockOwnerImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockOwnerImpl.java new file mode 100644 index 0000000000..3509e5fb46 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockOwnerImpl.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.common.lock; + +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; + +import org.eclipse.net4j.util.ObjectUtil; + +/** + * @author Caspar De Groot + */ +public class CDOLockOwnerImpl implements CDOLockOwner +{ + private final int sessionID; + + private final int viewID; + + public CDOLockOwnerImpl(int sessionID, int viewID) + { + this.sessionID = sessionID; + this.viewID = viewID; + } + + public int getSessionID() + { + return sessionID; + } + + public int getViewID() + { + return viewID; + } + + @Override + public int hashCode() + { + return ObjectUtil.hashCode(sessionID, viewID); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + { + return true; + } + + if (obj instanceof CDOLockOwner) + { + CDOLockOwner that = (CDOLockOwner)obj; + return sessionID == that.getSessionID() && viewID == that.getViewID(); + } + + return false; + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder("CDOLockOwner["); + builder.append("session="); + builder.append(sessionID); + builder.append(", view="); + builder.append(viewID); + builder.append(']'); + return builder.toString(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java new file mode 100644 index 0000000000..3520709d0a --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.common.lock; + +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState; + +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Caspar De Groot + */ +public class CDOLockStateImpl implements InternalCDOLockState +{ + private final Object lockedObject; + + private final Set<CDOLockOwner> readLockOwners = new HashSet<CDOLockOwner>(); + + private CDOLockOwner writeLockOwner; + + private CDOLockOwner writeOptionOwner; + + public CDOLockStateImpl(Object lockedObject) + { + CheckUtil.checkArg(lockedObject, "lockedObject"); + this.lockedObject = lockedObject; + } + + public boolean isLocked(LockType lockType, CDOLockOwner lockOwner, boolean others) + { + switch (lockType) + { + case READ: + return isReadLocked(lockOwner, others); + + case WRITE: + return isWriteLocked(lockOwner, others); + + case OPTION: + return isOptionLocked(lockOwner, others); + } + + return false; + } + + private boolean isReadLocked(CDOLockOwner by, boolean others) + { + if (readLockOwners.size() == 0) + { + return false; + } + + return readLockOwners.contains(by) ^ others; + } + + private boolean isWriteLocked(CDOLockOwner by, boolean others) + { + if (writeLockOwner == null) + { + return false; + } + + return writeLockOwner.equals(by) ^ others; + } + + private boolean isOptionLocked(CDOLockOwner by, boolean others) + { + if (writeOptionOwner == null) + { + return false; + } + + return writeOptionOwner.equals(by) ^ others; + } + + public Set<CDOLockOwner> getReadLockOwners() + { + return Collections.unmodifiableSet(readLockOwners); + } + + public CDOLockOwner getWriteLockOwner() + { + return writeLockOwner; + } + + public void setWriteLockOwner(CDOLockOwner lockOwner) + { + writeLockOwner = lockOwner; + } + + public CDOLockOwner getWriteOptionOwner() + { + return writeOptionOwner; + } + + public void setWriteOptionOwner(CDOLockOwner lockOwner) + { + writeOptionOwner = lockOwner; + } + + public Object getLockedObject() + { + return lockedObject; + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder("CDOLockState[lockedObject="); + builder.append(lockedObject); + + if (readLockOwners.size() > 0) + { + builder.append(", read="); + boolean first = true; + for (CDOLockOwner lockOwner : readLockOwners) + { + if (first) + { + first = false; + } + else + { + builder.append(", "); + } + + builder.append(lockOwner); + } + + builder.deleteCharAt(builder.length() - 1); + } + + if (writeLockOwner != null) + { + builder.append(", write="); + builder.append(writeLockOwner); + } + + if (writeOptionOwner != null) + { + builder.append(", option="); + builder.append(writeOptionOwner); + } + + builder.append(']'); + return builder.toString(); + } + + public void addReadLockOwner(CDOLockOwner lockOwner) + { + readLockOwners.add(lockOwner); + } + + public boolean removeReadLockOwner(CDOLockOwner lockOwner) + { + return readLockOwners.remove(lockOwner); + } +} diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataInputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataInputImpl.java index 6da3c339f2..d64799ed3e 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataInputImpl.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataInputImpl.java @@ -26,6 +26,10 @@ import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobStore; import org.eclipse.emf.cdo.common.lob.CDOLobUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageInfo; @@ -52,6 +56,9 @@ import org.eclipse.emf.cdo.internal.common.id.CDOIDExternalImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectLongImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDTempObjectExternalImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDTempObjectImpl; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockChangeInfoImpl; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockOwnerImpl; +import org.eclipse.emf.cdo.internal.common.lock.CDOLockStateImpl; import org.eclipse.emf.cdo.internal.common.messages.Messages; import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndBranchImpl; import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndVersionImpl; @@ -66,6 +73,7 @@ import org.eclipse.emf.cdo.internal.common.revision.delta.CDOSetFeatureDeltaImpl import org.eclipse.emf.cdo.internal.common.revision.delta.CDOUnsetFeatureDeltaImpl; import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; import org.eclipse.emf.cdo.spi.common.id.AbstractCDOID; +import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; @@ -255,6 +263,73 @@ public abstract class CDODataInputImpl extends ExtendedDataInput.Delegating impl return new FailureCommitInfo(timeStamp, previousTimeStamp); } + public CDOLockChangeInfo readCDOLockChangeInfo() throws IOException + { + CDOBranchPoint branchPoint = readCDOBranchPoint(); + CDOLockOwner lockOwner = readCDOLockOwner(); + Operation operation = readEnum(Operation.class); + + int n = readInt(); + CDOLockState[] lockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + lockStates[i] = readCDOLockState(); + } + + return new CDOLockChangeInfoImpl(branchPoint, lockOwner, lockStates, operation); + } + + public CDOLockOwner readCDOLockOwner() throws IOException + { + boolean isUnknown = !readBoolean(); + if (isUnknown) + { + return CDOLockOwner.UNKNOWN; + } + int session = readInt(); + int view = readInt(); + return new CDOLockOwnerImpl(session, view); + } + + public CDOLockState readCDOLockState() throws IOException + { + Object target; + boolean sendingBranchWithID = readBoolean(); + if (!sendingBranchWithID) + { + target = readCDOID(); + } + else + { + target = readCDOIDAndBranch(); + } + + InternalCDOLockState lockState = new CDOLockStateImpl(target); + + int nReadLockOwners = readInt(); + for (int i = 0; i < nReadLockOwners; i++) + { + CDOLockOwner lockOwner = readCDOLockOwner(); + lockState.addReadLockOwner(lockOwner); + } + + boolean hasWriteLock = readBoolean(); + if (hasWriteLock) + { + CDOLockOwner lockOwner = readCDOLockOwner(); + lockState.setWriteLockOwner(lockOwner); + } + + boolean hasWriteOption = readBoolean(); + if (hasWriteOption) + { + CDOLockOwner lockOwner = readCDOLockOwner(); + lockState.setWriteOptionOwner(lockOwner); + } + + return lockState; + } + public CDOID readCDOID() throws IOException { byte ordinal = readByte(); @@ -514,7 +589,8 @@ public abstract class CDODataInputImpl extends ExtendedDataInput.Delegating impl public LockType readCDOLockType() throws IOException { - return readBoolean() ? LockType.WRITE : LockType.READ; + byte b = readByte(); + return b == 0 ? null : LockType.values()[b - 1]; } protected StringIO getPackageURICompressor() diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataOutputImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataOutputImpl.java index 394aad8048..9ceed91d70 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataOutputImpl.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/protocol/CDODataOutputImpl.java @@ -19,6 +19,9 @@ import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageInfo; @@ -63,6 +66,7 @@ import org.eclipse.emf.ecore.util.FeatureMapUtil; import java.io.IOException; import java.text.MessageFormat; import java.util.Collection; +import java.util.Set; /** * @author Eike Stepper @@ -218,6 +222,82 @@ public abstract class CDODataOutputImpl extends ExtendedDataOutput.Delegating im } } + public void writeCDOLockChangeInfo(CDOLockChangeInfo lockChangeInfo) throws IOException + { + writeCDOBranchPoint(lockChangeInfo); + writeCDOLockOwner(lockChangeInfo.getLockOwner()); + writeEnum(lockChangeInfo.getOperation()); + + CDOLockState[] lockStates = lockChangeInfo.getLockStates(); + writeInt(lockStates.length); + for (CDOLockState lockState : lockStates) + { + writeCDOLockState(lockState); + } + } + + public void writeCDOLockOwner(CDOLockOwner lockOwner) throws IOException + { + if (lockOwner != CDOLockOwner.UNKNOWN) + { + writeBoolean(true); + writeInt(lockOwner.getSessionID()); + writeInt(lockOwner.getViewID()); + } + else + { + writeBoolean(false); + } + } + + public void writeCDOLockState(CDOLockState lockState) throws IOException + { + Object o = lockState.getLockedObject(); + if (o instanceof CDOID) + { + writeBoolean(false); + writeCDOID((CDOID)o); + } + else if (o instanceof CDOIDAndBranch) + { + writeBoolean(true); + writeCDOIDAndBranch((CDOIDAndBranch)o); + } + else + { + throw new AssertionError("Unexpected type: " + o.getClass().getSimpleName()); + } + + Set<CDOLockOwner> readLockOwners = lockState.getReadLockOwners(); + writeInt(readLockOwners.size()); + for (CDOLockOwner readLockOwner : readLockOwners) + { + writeCDOLockOwner(readLockOwner); + } + + CDOLockOwner writeLockOwner = lockState.getWriteLockOwner(); + if (writeLockOwner != null) + { + writeBoolean(true); + writeCDOLockOwner(writeLockOwner); + } + else + { + writeBoolean(false); + } + + CDOLockOwner writeOptionOwner = lockState.getWriteOptionOwner(); + if (writeOptionOwner != null) + { + writeBoolean(true); + writeCDOLockOwner(writeOptionOwner); + } + else + { + writeBoolean(false); + } + } + public void writeCDOID(CDOID id) throws IOException { if (id == null) @@ -438,7 +518,8 @@ public abstract class CDODataOutputImpl extends ExtendedDataOutput.Delegating im public void writeCDOLockType(LockType lockType) throws IOException { - writeBoolean(lockType == LockType.WRITE ? true : false); + int b = lockType == null ? 0 : lockType.ordinal() + 1; + writeByte(b); } public CDOPackageRegistry getPackageRegistry() diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java new file mode 100644 index 0000000000..dddd9f7713 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.common.lock; + +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; + +/** + * @author Caspar De Groot + * @since 4.1 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface InternalCDOLockState extends CDOLockState +{ + public void addReadLockOwner(CDOLockOwner lockOwner); + + public boolean removeReadLockOwner(CDOLockOwner lockOwner); + + public void setWriteLockOwner(CDOLockOwner lockOwner); + + public void setWriteOptionOwner(CDOLockOwner lockOwner); +} diff --git a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/AbstractOfflineExampleServer.java b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/AbstractOfflineExampleServer.java index ac775d09de..c7f3f649dd 100644 --- a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/AbstractOfflineExampleServer.java +++ b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/AbstractOfflineExampleServer.java @@ -124,10 +124,10 @@ public abstract class AbstractOfflineExampleServer {
for (;;)
{
- // System.out.println();
- // System.out.println("Enter a command:");
- // showMenu();
- // System.out.println();
+ System.out.println();
+ System.out.println("Enter a command:");
+ showMenu();
+ System.out.println();
String command = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (handleCommand(command))
@@ -144,6 +144,14 @@ public abstract class AbstractOfflineExampleServer container.deactivate();
}
+ protected void showMenu()
+ {
+ System.out.println("0 - exit");
+ System.out.println("1 - connect repository to network");
+ System.out.println("2 - disconnect repository from network");
+ System.out.println("3 - dump repository infos");
+ }
+
protected boolean handleCommand(String command)
{
if ("1".equals(command))
diff --git a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleClient.java b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleClient.java index a8a448486d..52d10208c5 100644 --- a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleClient.java +++ b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleClient.java @@ -10,6 +10,7 @@ */
package org.eclipse.emf.cdo.examples.server.offline;
+import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.common.CDOCommonRepository;
import org.eclipse.emf.cdo.common.CDOCommonRepository.State;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
@@ -21,6 +22,7 @@ import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; import org.eclipse.emf.cdo.net4j.CDONet4jUtil;
import org.eclipse.emf.cdo.session.CDORepositoryInfo;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
+import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.net4j.Net4jUtil;
@@ -28,7 +30,10 @@ import org.eclipse.net4j.connector.IConnector; import org.eclipse.net4j.util.container.IManagedContainer;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
import java.io.BufferedReader;
@@ -67,6 +72,42 @@ public class OfflineExampleClient }
}
+ private static void lockObject(CDOTransaction tx)
+ {
+ EList<EObject> contents = tx.getOrCreateResource("/r1").getContents();
+ int size = contents.size();
+ if (size < 1)
+ {
+ System.out.println("There are no objects; can't lock anything.");
+ }
+
+ System.out.println("Locking last object");
+ CDOObject firstObject = CDOUtil.getCDOObject(contents.get(size - 1));
+ firstObject.cdoWriteLock().lock();
+ System.out.println("Locked last object");
+ }
+
+ private static void unlockObject(CDOTransaction tx)
+ {
+ EList<EObject> contents = tx.getOrCreateResource("/r1").getContents();
+ int size = contents.size();
+ if (size < 1)
+ {
+ System.out.println("There are no objects; can't lock anything.");
+ }
+
+ System.out.println("Unlocking last object");
+ CDOObject firstObject = CDOUtil.getCDOObject(contents.get(size - 1));
+ firstObject.cdoWriteLock().unlock();
+ System.out.println("Unlocked last object");
+ }
+
+ private static void createBranch(CDOTransaction tx)
+ {
+ CDOBranch subBranch = tx.getBranch().createBranch("sub.1");
+ tx.setBranch(subBranch);
+ }
+
private static boolean isAutoMerge(String[] args)
{
for (int i = 0; i < args.length; i++)
@@ -150,12 +191,44 @@ public class OfflineExampleClient System.out.println("Connected to " + repositoryInfo.getName());
tx = session.openTransaction();
+ tx.enableDurableLocking(true);
createSessionListener(session, autoMerging);
for (;;)
{
- new BufferedReader(new InputStreamReader(System.in)).readLine();
- addObject(tx);
+ System.out.println();
+ System.out.println("Enter a command:");
+ System.out.println("0 - exit");
+ System.out.println("1 - add an object to the repository");
+ System.out.println("2 - lock the last object in the repository");
+ System.out.println("3 - unlock the last object in the repository");
+ System.out.println("4 - create a branch");
+
+ String command = new BufferedReader(new InputStreamReader(System.in)).readLine();
+ if ("0".equals(command))
+ {
+ break;
+ }
+
+ if ("1".equals(command))
+ {
+ addObject(tx);
+ }
+ else if ("2".equals(command))
+ {
+ lockObject(tx);
+ }
+ else if ("3".equals(command))
+ {
+ unlockObject(tx);
+ }
+ else if ("4".equals(command))
+ {
+ createBranch(tx);
+ }
}
+
+ session.close();
+ LifecycleUtil.deactivate(container);
}
}
diff --git a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleMaster.java b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleMaster.java index 20dcd4bba8..6147a90d7f 100644 --- a/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleMaster.java +++ b/plugins/org.eclipse.emf.cdo.examples/src/org/eclipse/emf/cdo/examples/server/offline/OfflineExampleMaster.java @@ -40,14 +40,6 @@ public class OfflineExampleMaster extends AbstractOfflineExampleServer return CDOServerUtil.createRepository(name, store, props);
}
- protected void showMenu()
- {
- System.out.println("0 - exit");
- System.out.println("1 - connect repository to network");
- System.out.println("2 - disconnect repository from network");
- System.out.println("3 - dump repository infos");
- }
-
public static void main(String[] args) throws Exception
{
System.out.println("Master repository starting...");
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java index aeddc4832d..9ba6ae8b50 100644 --- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java @@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; @@ -66,6 +67,7 @@ import org.eclipse.emf.spi.cdo.InternalCDOXATransaction.InternalCDOXACommitConte import java.io.IOException; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -218,15 +220,66 @@ public class CDOClientProtocol extends SignalProtocol<CDOSession> implements CDO } } - public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch, + @Deprecated + public LockObjectsResult lockObjects(List<InternalCDORevision> revisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException { + List<CDORevisionKey> revisionKeys = new LinkedList<CDORevisionKey>(); + for (InternalCDORevision rev : revisions) + { + revisionKeys.add(rev); + } + + return lockObjects2(revisionKeys, viewID, viewedBranch, lockType, timeout); + } + + public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException + { + InterruptedException interruptedException = null; + RuntimeException runtimeException = null; + + try + { + return new LockObjectsRequest(this, revisionKeys, viewID, viewedBranch, lockType, timeout).send(); + } + catch (RemoteException ex) + { + if (ex.getCause() instanceof RuntimeException) + { + runtimeException = (RuntimeException)ex.getCause(); + } + else if (ex.getCause() instanceof InterruptedException) + { + interruptedException = (InterruptedException)ex.getCause(); + } + else + { + runtimeException = WrappedException.wrap(ex); + } + } + catch (Exception ex) + { + throw WrappedException.wrap(ex); + } + + if (interruptedException != null) + { + throw interruptedException; + } + + throw runtimeException; + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException + { InterruptedException interruptedException = null; RuntimeException runtimeException = null; try { - return new LockObjectsRequest(this, viewedRevisions, viewID, viewedBranch, lockType, timeout).send(); + return new LockDelegationRequest(this, lockAreaID, revisionKeys, viewedBranch, lockType, timeout).send(); } catch (RemoteException ex) { @@ -256,9 +309,19 @@ public class CDOClientProtocol extends SignalProtocol<CDOSession> implements CDO throw runtimeException; } - public void unlockObjects(CDOView view, Collection<? extends CDOObject> objects, LockType lockType) + public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType) { - send(new UnlockObjectsRequest(this, view, objects, lockType)); + send(new UnlockObjectsRequest(this, view.getViewID(), objectIDs, lockType)); + } + + public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + return send(new UnlockObjectsRequest(this, view.getViewID(), objectIDs, lockType)); + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType) + { + return send(new UnlockDelegationRequest(this, lockAreaID, objectIDs, lockType)); } public boolean isObjectLocked(CDOView view, CDOObject object, LockType lockType, boolean byOthers) @@ -398,6 +461,9 @@ public class CDOClientProtocol extends SignalProtocol<CDOSession> implements CDO case CDOProtocolConstants.SIGNAL_REMOTE_MESSAGE_NOTIFICATION: return new RemoteMessageNotificationIndication(this); + case CDOProtocolConstants.SIGNAL_LOCK_NOTIFICATION: + return new LockNotificationIndication(this); + default: return super.createSignalReactor(signalID); } @@ -447,4 +513,14 @@ public class CDOClientProtocol extends SignalProtocol<CDOSession> implements CDO REVISION_LOADING.stop(request); } } + + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) + { + return send(new LockStateRequest(this, viewID, ids)); + } + + public void enableLockNotifications(int viewID, boolean on) + { + send(new EnableLockNotificationRequest(this, viewID, on)); + } } diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java index 00760654ab..5da87ff235 100644 --- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitTransactionRequest.java @@ -26,6 +26,7 @@ import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lob.CDOBlob; import org.eclipse.emf.cdo.common.lob.CDOClob; import org.eclipse.emf.cdo.common.lob.CDOLob; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; @@ -229,6 +230,7 @@ public class CommitTransactionRequest extends CDOClientRequestWithMonitoring<Com result = confirmingResult(in); confirmingMappingNewObjects(in, result); + confirmingNewLockStates(in, result); return result; } @@ -290,4 +292,17 @@ public class CommitTransactionRequest extends CDOClientRequestWithMonitoring<Com } } } + + protected void confirmingNewLockStates(CDODataInput in, CommitTransactionResult result) throws IOException + { + int n = in.readInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + + for (int i = 0; i < n; i++) + { + newLockStates[i] = in.readCDOLockState(); + } + + result.setNewLockStates(newLockStates); + } } diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java new file mode 100644 index 0000000000..47530f52a2 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/EnableLockNotificationRequest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class EnableLockNotificationRequest extends CDOClientRequest<Boolean> +{ + private int viewID; + + private boolean on; + + public EnableLockNotificationRequest(CDOClientProtocol protocol, int viewID, boolean on) + { + super(protocol, CDOProtocolConstants.SIGNAL_ENABLE_LOCK_NOTIFICATION); + this.viewID = viewID; + this.on = on; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeInt(viewID); + out.writeBoolean(on); + } + + @Override + protected Boolean confirming(CDODataInput in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java new file mode 100644 index 0000000000..08be5b6615 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockDelegationRequest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.io.IOException; +import java.util.List; + +/** + * @author Caspar De Groot + */ +public class LockDelegationRequest extends LockObjectsRequest +{ + private String lockAreaID; + + public LockDelegationRequest(CDOClientProtocol protocol, String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_DELEGATION, revisionKeys, 0, viewedBranch, lockType, timeout); + this.lockAreaID = lockAreaID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeString(lockAreaID); + super.requesting(out); + } +} diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java new file mode 100644 index 0000000000..2d4175c8ef --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockNotificationIndication.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class LockNotificationIndication extends CDOClientIndication +{ + public LockNotificationIndication(CDOClientProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + CDOLockChangeInfo lockChangeInfo = in.readCDOLockChangeInfo(); + InternalCDOSession session = getSession(); + session.handleLockNotification(lockChangeInfo); + } +} diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java index ffe3ca718c..2d8e9325d9 100644 --- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockObjectsRequest.java @@ -12,13 +12,11 @@ package org.eclipse.emf.cdo.internal.net4j.protocol; import org.eclipse.emf.cdo.common.branch.CDOBranch; -import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; -import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; -import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.net4j.util.concurrent.IRWLockManager; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; @@ -39,22 +37,28 @@ public class LockObjectsRequest extends CDOClientRequest<LockObjectsResult> private long timeout; - private List<InternalCDORevision> viewedRevisions; + private List<CDORevisionKey> revisionKeys; /** * The branch being viewed */ private CDOBranch viewedBranch; - public LockObjectsRequest(CDOClientProtocol protocol, List<InternalCDORevision> viewedRevisions, int viewID, + public LockObjectsRequest(CDOClientProtocol protocol, List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) { - super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS); + this(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS, revisionKeys, viewID, viewedBranch, lockType, timeout); + } + + protected LockObjectsRequest(CDOClientProtocol protocol, short signalID, List<CDORevisionKey> revisionKeys, + int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) + { + super(protocol, signalID); this.viewID = viewID; this.lockType = lockType; this.timeout = timeout; - this.viewedRevisions = viewedRevisions; + this.revisionKeys = revisionKeys; this.viewedBranch = viewedBranch; } @@ -66,10 +70,10 @@ public class LockObjectsRequest extends CDOClientRequest<LockObjectsResult> out.writeLong(timeout); out.writeCDOBranch(viewedBranch); - out.writeInt(viewedRevisions.size()); - for (CDORevision revision : viewedRevisions) + out.writeInt(revisionKeys.size()); + for (CDORevisionKey revKey : revisionKeys) { - out.writeCDORevisionKey(revision); + out.writeCDORevisionKey(revKey); } } @@ -77,23 +81,9 @@ public class LockObjectsRequest extends CDOClientRequest<LockObjectsResult> protected LockObjectsResult confirming(CDODataInput in) throws IOException { boolean succesful = in.readBoolean(); - if (succesful) - { - boolean clientMustWait = in.readBoolean(); - long requiredTimestamp = CDOBranchPoint.UNSPECIFIED_DATE; - if (clientMustWait) - { - requiredTimestamp = in.readLong(); - } - - return new LockObjectsResult(true, false, clientMustWait, requiredTimestamp, null); - } - - boolean timedOut = in.readBoolean(); - if (timedOut) - { - return new LockObjectsResult(false, true, false, 0, null); - } + boolean timeout = in.readBoolean(); + boolean waitForUpdate = in.readBoolean(); + long requiredTimestamp = in.readLong(); int nStaleRevisions = in.readInt(); CDORevisionKey[] staleRevisions = new CDORevisionKey[nStaleRevisions]; @@ -102,6 +92,13 @@ public class LockObjectsRequest extends CDOClientRequest<LockObjectsResult> staleRevisions[i] = in.readCDORevisionKey(); } - return new LockObjectsResult(false, false, false, 0, staleRevisions); + int n = in.readInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + newLockStates[i] = in.readCDOLockState(); + } + + return new LockObjectsResult(succesful, timeout, waitForUpdate, requiredTimestamp, staleRevisions, newLockStates); } } diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java new file mode 100644 index 0000000000..30e443d84f --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/LockStateRequest.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Caspar De Groot + */ +public class LockStateRequest extends CDOClientRequest<CDOLockState[]> +{ + private int viewID; + + private Collection<CDOID> ids; + + public LockStateRequest(CDOClientProtocol protocol, int viewID, Collection<CDOID> ids) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_STATE); + this.viewID = viewID; + this.ids = ids; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeInt(viewID); + out.writeInt(ids.size()); + for (CDOID id : ids) + { + out.writeCDOID(id); + } + } + + @Override + protected CDOLockState[] confirming(CDODataInput in) throws IOException + { + int n = in.readInt(); + CDOLockState[] lockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + lockStates[i] = in.readCDOLockState(); + } + + return lockStates; + } +} diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java new file mode 100644 index 0000000000..02491722d5 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockDelegationRequest.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; + +import java.io.IOException; +import java.util.Collection; + +/** + * @author Caspar De Groot + */ +public class UnlockDelegationRequest extends UnlockObjectsRequest +{ + private String lockAreaID; + + public UnlockDelegationRequest(CDOClientProtocol protocol, String lockAreaID, Collection<CDOID> objectIDs, + LockType lockType) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_DELEGATION, 0, objectIDs, lockType); + this.lockAreaID = lockAreaID; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeString(lockAreaID); + super.requesting(out); + } +} diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java index be03dfd9ae..0eefb367a3 100644 --- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnlockObjectsRequest.java @@ -10,52 +10,58 @@ **************************************************************************/ package org.eclipse.emf.cdo.internal.net4j.protocol; -import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.internal.net4j.bundle.OM; -import org.eclipse.emf.cdo.view.CDOView; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + import java.io.IOException; import java.util.Collection; /** * @author Simon McDuff */ -public class UnlockObjectsRequest extends CDOClientRequest<Boolean> +public class UnlockObjectsRequest extends CDOClientRequest<UnlockObjectsResult> { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, UnlockObjectsRequest.class); - private CDOView view; + private int viewID; - private Collection<? extends CDOObject> objects; + private Collection<CDOID> objectIDs; private LockType lockType; - public UnlockObjectsRequest(CDOClientProtocol protocol, CDOView view, Collection<? extends CDOObject> objects, + public UnlockObjectsRequest(CDOClientProtocol protocol, int viewID, Collection<CDOID> objects, LockType lockType) + { + this(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS, viewID, objects, lockType); + } + + protected UnlockObjectsRequest(CDOClientProtocol protocol, short signalID, int viewID, Collection<CDOID> objectIDs, LockType lockType) { - super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS); - this.view = view; - this.objects = objects; + super(protocol, signalID); + this.viewID = viewID; + this.objectIDs = objectIDs; this.lockType = lockType; } @Override protected void requesting(CDODataOutput out) throws IOException { - out.writeInt(view.getViewID()); + out.writeInt(viewID); out.writeCDOLockType(lockType); - if (objects == null) + if (objectIDs == null) { if (TRACER.isEnabled()) { - TRACER.format("Unlocking all objects for view {0}", view.getViewID()); //$NON-NLS-1$ + TRACER.format("Unlocking all objects for view {0}", viewID); //$NON-NLS-1$ } out.writeInt(CDOProtocolConstants.RELEASE_ALL_LOCKS); @@ -65,13 +71,12 @@ public class UnlockObjectsRequest extends CDOClientRequest<Boolean> if (TRACER.isEnabled()) { TRACER.format("Unlocking of type {0} requested for view {1}", lockType == LockType.READ ? "read" //$NON-NLS-1$ //$NON-NLS-2$ - : "write", view.getViewID()); //$NON-NLS-1$ + : "write", viewID); //$NON-NLS-1$ } - out.writeInt(objects.size()); - for (CDOObject object : objects) + out.writeInt(objectIDs.size()); + for (CDOID id : objectIDs) { - CDOID id = object.cdoID(); if (TRACER.isEnabled()) { TRACER.format("Unlocking requested for object {0}", id); //$NON-NLS-1$ @@ -83,8 +88,14 @@ public class UnlockObjectsRequest extends CDOClientRequest<Boolean> } @Override - protected Boolean confirming(CDODataInput in) throws IOException + protected UnlockObjectsResult confirming(CDODataInput in) throws IOException { - return in.readBoolean(); + int n = in.readInt(); + CDOLockState[] newLockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + newLockStates[i] = in.readCDOLockState(); + } + return new UnlockObjectsResult(newLockStates); } } diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java index 80599796e1..dfad801b99 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java @@ -37,7 +37,7 @@ import org.eclipse.emf.cdo.server.IQueryHandler; import org.eclipse.emf.cdo.server.IRepository; import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.server.IStoreAccessor; -import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; import org.eclipse.emf.cdo.server.ITransaction; import org.eclipse.emf.cdo.server.db.CDODBUtil; import org.eclipse.emf.cdo.server.db.IDBStore; @@ -108,7 +108,7 @@ import java.util.TimerTask; /** * @author Eike Stepper */ -public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking +public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking2 { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class); @@ -1291,8 +1291,14 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks) { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { DurableLockingManager manager = getStore().getDurableLockingManager(); - return manager.createLockArea(this, userID, branchPoint, readOnly, locks); + return manager.createLockArea(this, durableLockingID, userID, branchPoint, readOnly, locks); } public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java index 594ff9964c..789e425a75 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DurableLockingManager.java @@ -14,6 +14,7 @@ import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea;
import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
+import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaAlreadyExistsException;
import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException;
import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade;
import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
@@ -102,12 +103,28 @@ public class DurableLockingManager extends Lifecycle this.store = store;
}
- public synchronized LockArea createLockArea(DBStoreAccessor accessor, String userID, CDOBranchPoint branchPoint,
- boolean readOnly, Map<CDOID, LockGrade> locks)
+ public synchronized LockArea createLockArea(DBStoreAccessor accessor, String durableLockingID, String userID,
+ CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks)
{
try
{
- String durableLockingID = getNextDurableLockingID(accessor);
+ if (durableLockingID == null)
+ {
+ durableLockingID = getNextDurableLockingID(accessor);
+ }
+ else
+ {
+ // If the caller is specifying the ID, make sure there is no area with this ID yet
+ //
+ try
+ {
+ getLockArea(accessor, durableLockingID);
+ throw new LockAreaAlreadyExistsException(durableLockingID);
+ }
+ catch (LockAreaNotFoundException good)
+ {
+ }
+ }
IPreparedStatementCache statementCache = accessor.getStatementCache();
PreparedStatement stmt = null;
diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java index 3f96f29834..a0fc0cbb82 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java @@ -16,6 +16,7 @@ package org.eclipse.emf.cdo.server.internal.net4j.protocol; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.server.IRepositoryProvider; import org.eclipse.emf.cdo.server.internal.net4j.bundle.OM; @@ -154,6 +155,18 @@ public class CDOServerProtocol extends SignalProtocol<InternalSession> implement } } + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception + { + if (LifecycleUtil.isActive(getChannel())) + { + new LockNotificationRequest(this, lockChangeInfo).sendAsync(); + } + else + { + handleInactiveSession(); + } + } + protected void handleInactiveSession() { OM.LOG.warn("Session channel is inactive: " + this); //$NON-NLS-1$ @@ -251,6 +264,12 @@ public class CDOServerProtocol extends SignalProtocol<InternalSession> implement case CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS: return new UnlockObjectsIndication(this); + case CDOProtocolConstants.SIGNAL_LOCK_DELEGATION: + return new LockDelegationIndication(this); + + case CDOProtocolConstants.SIGNAL_UNLOCK_DELEGATION: + return new UnlockDelegationIndication(this); + case CDOProtocolConstants.SIGNAL_OBJECT_LOCKED: return new ObjectLockedIndication(this); @@ -287,6 +306,12 @@ public class CDOServerProtocol extends SignalProtocol<InternalSession> implement case CDOProtocolConstants.SIGNAL_HANDLE_REVISIONS: return new HandleRevisionsIndication(this); + case CDOProtocolConstants.SIGNAL_LOCK_STATE: + return new LockStateIndication(this); + + case CDOProtocolConstants.SIGNAL_ENABLE_LOCK_NOTIFICATION: + return new EnableLockNotificationIndication(this); + default: return super.createSignalReactor(signalID); } diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java index 7aa169ab3e..959e665695 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CommitTransactionIndication.java @@ -15,10 +15,13 @@ package org.eclipse.emf.cdo.server.internal.net4j.protocol; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; import org.eclipse.emf.cdo.common.model.EMFUtil; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.server.internal.net4j.bundle.OM; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; @@ -29,6 +32,7 @@ import org.eclipse.emf.cdo.spi.server.InternalTransaction; import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.om.monitor.ProgressDistributor; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -256,6 +260,7 @@ public class CommitTransactionIndication extends CDOServerIndicationWithMonitori { respondingResult(out); respondingMappingNewObjects(out); + respondingNewLockStates(out); } } finally @@ -312,6 +317,24 @@ public class CommitTransactionIndication extends CDOServerIndicationWithMonitori out.writeCDOID(CDOID.NULL); } + protected void respondingNewLockStates(CDODataOutput out) throws Exception + { + List<LockState<Object, IView>> newLockStates = commitContext.getPostCommmitLockStates(); + if (newLockStates != null) + { + out.writeInt(newLockStates.size()); + for (LockState<Object, IView> lockState : newLockStates) + { + CDOLockState cdoLockState = CDOLockUtil.createLockState(lockState); + out.writeCDOLockState(cdoLockState); + } + } + else + { + out.writeInt(0); + } + } + protected InternalTransaction getTransaction(int viewID) { InternalView view = getSession().getView(viewID); diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/EnableLockNotificationIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/EnableLockNotificationIndication.java new file mode 100644 index 0000000000..34ca52a802 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/EnableLockNotificationIndication.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class EnableLockNotificationIndication extends CDOServerIndication +{ + public EnableLockNotificationIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_ENABLE_LOCK_NOTIFICATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int viewID = in.readInt(); + boolean enable = in.readBoolean(); + + InternalView view = getSession().getView(viewID); + view.setLockNotificationEnabled(enable); + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + out.writeBoolean(true); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockDelegationIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockDelegationIndication.java new file mode 100644 index 0000000000..eaa67fd485 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockDelegationIndication.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockAreaNotFoundException; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.CheckUtil; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class LockDelegationIndication extends LockObjectsIndication +{ + private InternalView view; + + private String lockAreaID; + + public LockDelegationIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_DELEGATION); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + lockAreaID = in.readString(); + super.indicating(in); + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + try + { + super.responding(out); + } + finally + { + view.close(); + } + } + + @Override + protected IView getView(int viewID, CDOBranch viewedBranch) + { + // The view needs a lockArea... + InternalLockManager lockManager = getRepository().getLockManager(); + InternalSession session = getSession(); + LockArea lockArea; + + try + { + lockArea = lockManager.getLockArea(lockAreaID); + + // If we get here, the lockArea already exists. + view = (InternalView)lockManager.openView(session, InternalSession.TEMP_VIEW_ID, true, lockAreaID); + } + catch (LockAreaNotFoundException e) + { + // If we get here, the lockArea does not yet exist on the master, so we open + // a view without a lockArea first, then create a lockArea with the given ID, + // and associate it with the view. + view = session.openView(InternalSession.TEMP_VIEW_ID, viewedBranch.getHead()); + lockArea = lockManager.createLockArea(view, lockAreaID); + view.setDurableLockingID(lockAreaID); + } + + // The viewID received as an argument, is the ID of the client's view, which + // does not exist on the master. So we ignore this argument and open a new + // view instead. + CheckUtil.checkState(lockAreaID.equals(lockArea.getDurableLockingID()), "lockAreaID has incorrect value"); + + return view; + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockNotificationRequest.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockNotificationRequest.java new file mode 100644 index 0000000000..17314e9532 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockNotificationRequest.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class LockNotificationRequest extends CDOServerRequest +{ + private CDOLockChangeInfo lockChangeInfo; + + public LockNotificationRequest(CDOServerProtocol serverProtocol, CDOLockChangeInfo lockChangeInfo) + { + super(serverProtocol, CDOProtocolConstants.SIGNAL_LOCK_NOTIFICATION); + this.lockChangeInfo = lockChangeInfo; + } + + @Override + protected void requesting(CDODataOutput out) throws IOException + { + out.writeCDOLockChangeInfo(lockChangeInfo); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java index 236c74455e..b61cc1cf8b 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockObjectsIndication.java @@ -13,24 +13,20 @@ package org.eclipse.emf.cdo.server.internal.net4j.protocol; import org.eclipse.emf.cdo.common.branch.CDOBranch; -import org.eclipse.emf.cdo.common.id.CDOID; -import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; -import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.server.IView; -import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; -import org.eclipse.emf.cdo.spi.server.InternalLockManager; -import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalView; -import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; -import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; + +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; import java.io.IOException; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -39,138 +35,63 @@ import java.util.List; */ public class LockObjectsIndication extends CDOServerWriteIndication { - private List<CDORevisionKey> staleRevisions = new LinkedList<CDORevisionKey>(); - - private boolean timedOut; - - private boolean passiveUpdatesEnabled; - - private long requiredTimestamp; - - private boolean staleNoUpdate; + private LockObjectsResult result; public LockObjectsIndication(CDOServerProtocol protocol) { super(protocol, CDOProtocolConstants.SIGNAL_LOCK_OBJECTS); } + protected LockObjectsIndication(CDOServerProtocol protocol, short signalID) + { + super(protocol, signalID); + } + @Override protected void indicating(CDODataInput in) throws IOException { - InternalSession session = getSession(); - passiveUpdatesEnabled = session.isPassiveUpdateEnabled(); - int viewID = in.readInt(); LockType lockType = in.readCDOLockType(); long timeout = in.readLong(); CDOBranch viewedBranch = in.readCDOBranch(); int nRevisions = in.readInt(); - CDORevisionKey[] revKeys = new CDORevisionKey[nRevisions]; + List<CDORevisionKey> revisionKeys = new LinkedList<CDORevisionKey>(); for (int i = 0; i < nRevisions; i++) { - revKeys[i] = in.readCDORevisionKey(); + revisionKeys.add(in.readCDORevisionKey()); } - List<Object> objectsToBeLocked = new ArrayList<Object>(); - boolean isSupportingBranches = getRepository().isSupportingBranches(); - for (CDORevisionKey revKey : revKeys) - { - CDOID id = revKey.getID(); - if (isSupportingBranches) - { - objectsToBeLocked.add(CDOIDUtil.createIDAndBranch(id, viewedBranch)); - } - else - { - objectsToBeLocked.add(id); - } - } - - IView view = session.getView(viewID); - InternalLockManager lockManager = getRepository().getLockManager(); - - try - { - lockManager.lock(true, lockType, view, objectsToBeLocked, timeout); - } - catch (TimeoutRuntimeException ex) - { - timedOut = true; - return; - } - catch (InterruptedException ex) - { - throw WrappedException.wrap(ex); - } - - try - { - for (CDORevisionKey revKey : revKeys) - { - checkStale(viewedBranch, revKey); - } - } - catch (IllegalArgumentException ex) - { - lockManager.unlock(true, lockType, view, objectsToBeLocked); - throw ex; - } - - // If some of the clients' revisions are stale and it has passiveUpdates disabled, - // then the locks are useless so we release them and report the stale revisions (later) - staleNoUpdate = staleRevisions.size() > 0 && !passiveUpdatesEnabled; - if (staleNoUpdate) - { - lockManager.unlock(true, lockType, view, objectsToBeLocked); - } + InternalRepository repository = getRepository(); + IView view = getView(viewID, viewedBranch); + result = repository.lock((InternalView)view, lockType, revisionKeys, viewedBranch, timeout); } - @Override - protected void responding(CDODataOutput out) throws IOException + protected IView getView(int viewID, CDOBranch viewedBranch) { - boolean lockSuccesful = !timedOut && !staleNoUpdate; - out.writeBoolean(lockSuccesful); - - if (lockSuccesful) - { - boolean clientMustWait = staleRevisions.size() > 0; - out.writeBoolean(clientMustWait); - if (clientMustWait) - { - out.writeLong(requiredTimestamp); - } - } - else - { - out.writeBoolean(timedOut); - if (!timedOut) - { - out.writeInt(staleRevisions.size()); - for (CDORevisionKey staleRevision : staleRevisions) - { - out.writeCDORevisionKey(staleRevision); - } - } - } + return getSession().getView(viewID); } - private void checkStale(CDOBranch viewedBranch, CDORevisionKey revKey) + @Override + protected void responding(CDODataOutput out) throws IOException { - CDOID id = revKey.getID(); - InternalCDORevision rev = getRepository().getRevisionManager().getRevision(id, viewedBranch.getHead(), - CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); - - if (rev == null) + out.writeBoolean(result.isSuccessful()); + out.writeBoolean(result.isTimedOut()); + out.writeBoolean(result.isWaitForUpdate()); + out.writeLong(result.getRequiredTimestamp()); + + CDORevisionKey[] staleRevisions = result.getStaleRevisions(); + out.writeInt(staleRevisions.length); + for (CDORevisionKey revKey : staleRevisions) { - throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id, - viewedBranch)); + out.writeCDORevisionKey(revKey); } - if (!revKey.equals(rev)) + CDOLockState[] newLockStates = result.getNewLockStates(); + out.writeInt(newLockStates.length); + for (CDOLockState lockState : newLockStates) { - staleRevisions.add(revKey); - requiredTimestamp = Math.max(requiredTimestamp, rev.getTimeStamp()); + out.writeCDOLockState(lockState); } } } diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockStateIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockStateIndication.java new file mode 100644 index 0000000000..2ac03fbbcb --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/LockStateIndication.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class LockStateIndication extends CDOServerReadIndication +{ + private CDOLockState[] cdoLockStates; + + public LockStateIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_LOCK_STATE); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + int viewID = in.readInt(); + InternalView view = getSession().getView(viewID); + if (view == null) + { + throw new IllegalStateException("View not found"); + } + + InternalLockManager lockMgr = getRepository().getLockManager(); + + int n = in.readInt(); + cdoLockStates = new CDOLockState[n]; + for (int i = 0; i < n; i++) + { + Object key = indicatingCDOID(in, view.getBranch()); + LockState<Object, IView> lockState = lockMgr.getLockState(key); + if (lockState != null) + { + cdoLockStates[i] = CDOLockUtil.createLockState(lockState); + } + else + { + cdoLockStates[i] = CDOLockUtil.createLockState(key); + } + } + } + + private Object indicatingCDOID(CDODataInput in, CDOBranch viewedBranch) throws IOException + { + CDOID id = in.readCDOID(); + if (getRepository().isSupportingBranches()) + { + return CDOIDUtil.createIDAndBranch(id, viewedBranch); + } + + return id; + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + out.writeInt(cdoLockStates.length); + for (CDOLockState lockState : cdoLockStates) + { + out.writeCDOLockState(lockState); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/OpenViewIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/OpenViewIndication.java index d55c50c5c2..25c515f45c 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/OpenViewIndication.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/OpenViewIndication.java @@ -17,6 +17,7 @@ import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.spi.server.InternalLockManager; import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; import java.io.IOException; @@ -25,7 +26,7 @@ import java.io.IOException; */ public class OpenViewIndication extends CDOServerReadIndication { - private CDOBranchPoint result; + private InternalView newView; private String message; @@ -47,11 +48,11 @@ public class OpenViewIndication extends CDOServerReadIndication CDOBranchPoint branchPoint = in.readCDOBranchPoint(); if (readOnly) { - session.openView(viewID, branchPoint); + newView = session.openView(viewID, branchPoint); } else { - session.openTransaction(viewID, branchPoint); + newView = session.openTransaction(viewID, branchPoint); } } else @@ -61,7 +62,7 @@ public class OpenViewIndication extends CDOServerReadIndication try { String durableLockingID = in.readString(); - result = lockManager.openView(session, viewID, readOnly, durableLockingID); + newView = (InternalView)lockManager.openView(session, viewID, readOnly, durableLockingID); } catch (LockAreaNotFoundException ex) { @@ -77,10 +78,10 @@ public class OpenViewIndication extends CDOServerReadIndication @Override protected void responding(CDODataOutput out) throws IOException { - if (result != null) + if (newView != null) { out.writeBoolean(true); - out.writeCDOBranchPoint(result); + out.writeCDOBranchPoint(newView); } else { diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockDelegationIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockDelegationIndication.java new file mode 100644 index 0000000000..a33b2606fe --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockDelegationIndication.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.spi.server.InternalLockManager; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import java.io.IOException; + +/** + * @author Caspar De Groot + */ +public class UnlockDelegationIndication extends UnlockObjectsIndication +{ + private InternalView view; + + private String lockAreaID; + + public UnlockDelegationIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS); + } + + @Override + protected void indicating(CDODataInput in) throws IOException + { + lockAreaID = in.readString(); + super.indicating(in); + } + + @Override + protected void responding(CDODataOutput out) throws IOException + { + try + { + super.responding(out); + } + finally + { + view.close(); + } + } + + @Override + protected IView getView(int viewID) + { + InternalLockManager lockManager = getRepository().getLockManager(); + InternalSession session = getSession(); + view = (InternalView)lockManager.openView(session, InternalSession.TEMP_VIEW_ID, true, lockAreaID); + return view; + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockObjectsIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockObjectsIndication.java index 7c01c31d89..a945056014 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockObjectsIndication.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnlockObjectsIndication.java @@ -11,20 +11,21 @@ */ package org.eclipse.emf.cdo.server.internal.net4j.protocol; -import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID; -import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.server.IView; -import org.eclipse.emf.cdo.spi.server.InternalLockManager; import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; + import java.io.IOException; -import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; /** @@ -32,11 +33,18 @@ import java.util.List; */ public class UnlockObjectsIndication extends CDOServerWriteIndication { + private UnlockObjectsResult result; + public UnlockObjectsIndication(CDOServerProtocol protocol) { super(protocol, CDOProtocolConstants.SIGNAL_UNLOCK_OBJECTS); } + protected UnlockObjectsIndication(CDOServerProtocol protocol, short signalID) + { + super(protocol, signalID); + } + @Override protected void indicating(CDODataInput in) throws IOException { @@ -45,33 +53,37 @@ public class UnlockObjectsIndication extends CDOServerWriteIndication int size = in.readInt(); InternalRepository repository = getRepository(); - InternalLockManager lockManager = repository.getLockManager(); - IView view = getSession().getView(viewID); + IView view = getView(viewID); if (size == CDOProtocolConstants.RELEASE_ALL_LOCKS) { - lockManager.unlock(true, view); + result = repository.unlock((InternalView)view, null, null); } else { - boolean supportingBranches = repository.isSupportingBranches(); - CDOBranch branch = view.getBranch(); - - List<Object> keys = new ArrayList<Object>(size); + List<CDOID> objectIDs = new LinkedList<CDOID>(); for (int i = 0; i < size; i++) { - CDOID id = in.readCDOID(); - Object key = supportingBranches ? CDOIDUtil.createIDAndBranch(id, branch) : id; - keys.add(key); + objectIDs.add(in.readCDOID()); } - lockManager.unlock(true, lockType, view, keys); + result = repository.unlock((InternalView)view, lockType, objectIDs); } } @Override protected void responding(CDODataOutput out) throws IOException { - out.writeBoolean(true); + CDOLockState[] newLockStates = result.getNewLockStates(); + out.writeInt(newLockStates.length); + for (CDOLockState state : newLockStates) + { + out.writeCDOLockState(state); + } + } + + protected IView getView(int viewID) + { + return getSession().getView(viewID); } } 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 index 9322e1337b..f552982f9c 100644 --- 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 @@ -23,6 +23,7 @@ import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.server.ISessionManager; import org.eclipse.emf.cdo.server.IStoreAccessor; import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.server.StoreThreadLocal; import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; @@ -45,6 +46,7 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -155,9 +157,16 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern return result; } + @Deprecated public void lock(boolean explicit, LockType type, IView view, Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException { + lock2(explicit, type, view, objectsToLock, timeout); + } + + public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView view, + Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException + { String durableLockingID = null; DurableLocking accessor = null; @@ -170,16 +179,25 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern } } - super.lock(type, view, objectsToLock, timeout); + List<LockState<Object, IView>> newLockStates = super.lock2(type, view, objectsToLock, timeout); if (accessor != null) { accessor.lock(durableLockingID, type, objectsToLock); } + + return newLockStates; } + @Deprecated public void unlock(boolean explicit, LockType type, IView view, Collection<? extends Object> objectsToUnlock) { + unlock2(explicit, type, view, objectsToUnlock); + } + + public List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView view, + Collection<? extends Object> objectsToUnlock) + { if (explicit) { String durableLockingID = view.getDurableLockingID(); @@ -190,11 +208,17 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern } } - super.unlock(type, view, objectsToUnlock); + return super.unlock2(type, view, objectsToUnlock); } + @Deprecated public void unlock(boolean explicit, IView view) { + unlock(explicit, view); + } + + public List<LockState<Object, IView>> unlock2(boolean explicit, IView view) + { if (explicit) { String durableLockingID = view.getDurableLockingID(); @@ -205,24 +229,41 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern } } - super.unlock(view); + return super.unlock2(view); } public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks) { - DurableLocking accessor = getDurableLocking(); - return accessor.createLockArea(userID, branchPoint, readOnly, locks); + return createLockArea(userID, branchPoint, readOnly, locks, null); + } + + private LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks, String lockAreaID) + { + if (lockAreaID == null) + { + DurableLocking accessor = getDurableLocking(); + return accessor.createLockArea(userID, branchPoint, readOnly, locks); + } + + DurableLocking2 accessor = getDurableLocking2(); + return accessor.createLockArea(lockAreaID, userID, branchPoint, readOnly, locks); } public LockArea createLockArea(InternalView view) { + return createLockArea(view, null); + } + + public LockArea createLockArea(InternalView view, String lockAreaID) + { String userID = view.getSession().getUserID(); CDOBranchPoint branchPoint = CDOBranchUtil.copyBranchPoint(view); boolean readOnly = view.isReadOnly(); Map<CDOID, LockGrade> locks = getLocks(view); - LockArea area = createLockArea(userID, branchPoint, readOnly, locks); + LockArea area = createLockArea(userID, branchPoint, readOnly, locks, lockAreaID); synchronized (openViews) { openViews.put(area.getDurableLockingID(), view); @@ -328,7 +369,18 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern return (DurableLocking)accessor; } - throw new IllegalStateException("Store does not support durable locking"); + throw new IllegalStateException("Store does not implement " + DurableLocking.class.getSimpleName()); + } + + private DurableLocking2 getDurableLocking2() + { + IStoreAccessor accessor = StoreThreadLocal.getAccessor(); + if (accessor instanceof DurableLocking2) + { + return (DurableLocking2)accessor; + } + + throw new IllegalStateException("Store does not implement " + DurableLocking2.class.getSimpleName()); } private void loadDurableLocks() @@ -439,7 +491,7 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern public ISession getSession() { - throw new UnsupportedOperationException(); + return null; } @Override @@ -518,4 +570,21 @@ public class LockManager extends RWOLockManager<Object, IView> implements Intern return true; } } + + public LockGrade getLockGrade(Object key) + { + LockState<Object, IView> lockState = getObjectToLocksMap().get(key); + LockGrade grade = LockGrade.NONE; + if (lockState != null) + { + for (LockType type : LockType.values()) + { + if (lockState.hasLock(type)) + { + grade = grade.getUpdated(type, true); + } + } + } + return grade; + } } 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 79d2f213a0..60fac60b3a 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 @@ -15,6 +15,7 @@ */ package org.eclipse.emf.cdo.internal.server; +import org.eclipse.emf.cdo.common.CDOCommonView; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; @@ -28,6 +29,10 @@ import org.eclipse.emf.cdo.common.id.CDOIDGenerator; import org.eclipse.emf.cdo.common.id.CDOIDTemp; import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.model.EMFUtil; @@ -36,6 +41,7 @@ import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.common.util.CDOCommonUtil; import org.eclipse.emf.cdo.common.util.CDOQueryInfo; @@ -53,6 +59,7 @@ import org.eclipse.emf.cdo.server.IStoreAccessor; import org.eclipse.emf.cdo.server.IStoreChunkReader; import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.server.StoreThreadLocal; import org.eclipse.emf.cdo.spi.common.CDOReplicationContext; import org.eclipse.emf.cdo.spi.common.CDOReplicationInfo; @@ -81,13 +88,18 @@ import org.eclipse.emf.cdo.spi.server.InternalSession; import org.eclipse.emf.cdo.spi.server.InternalSessionManager; import org.eclipse.emf.cdo.spi.server.InternalStore; import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl; import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.collection.MoveableList; import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; import org.eclipse.net4j.util.container.Container; import org.eclipse.net4j.util.container.IPluginContainer; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; @@ -99,6 +111,8 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; import java.io.IOException; import java.io.OutputStream; @@ -107,6 +121,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -1325,6 +1340,147 @@ public class Repository extends Container<Object> implements InternalRepository } } + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, long timeout) + { + List<Object> objectsToLock = new ArrayList<Object>(); + boolean isSupportingBranches = isSupportingBranches(); + for (CDORevisionKey revKey : revisionKeys) + { + CDOID id = revKey.getID(); + if (isSupportingBranches) + { + objectsToLock.add(CDOIDUtil.createIDAndBranch(id, viewedBranch)); + } + else + { + objectsToLock.add(id); + } + } + + InternalLockManager lockManager = getLockManager(); + List<LockState<Object, IView>> newLockStates = null; + + try + { + newLockStates = lockManager.lock2(true, lockType, view, objectsToLock, timeout); + } + catch (TimeoutRuntimeException ex) + { + return new LockObjectsResult(false, true, false, 0, new CDORevisionKey[0], new CDOLockState[0]); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + + List<CDORevisionKey> staleRevisions = new LinkedList<CDORevisionKey>(); + long requiredTimestamp = 0; + + try + { + InternalCDORevisionManager revManager = getRevisionManager(); + + for (CDORevisionKey revKey : revisionKeys) + { + CDOID id = revKey.getID(); + InternalCDORevision rev = revManager.getRevision(id, viewedBranch.getHead(), CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + + if (rev == null) + { + throw new IllegalArgumentException(String.format("Object %s not found in branch %s (possibly detached)", id, + viewedBranch)); + } + + if (!revKey.equals(rev)) + { + staleRevisions.add(revKey); + requiredTimestamp = Math.max(requiredTimestamp, rev.getTimeStamp()); + } + } + } + catch (IllegalArgumentException ex) + { + lockManager.unlock2(true, lockType, view, objectsToLock); + throw ex; + } + + // Convert the list to an array, to satisfy the API later + // + CDORevisionKey[] staleRevisionsArray = new CDORevisionKey[staleRevisions.size()]; + staleRevisions.toArray(staleRevisionsArray); + + // If some of the clients' revisions are stale and it has passiveUpdates disabled, + // then the locks are useless so we release them and report the stale revisions + InternalSession session = view.getSession(); + boolean staleNoUpdate = staleRevisionsArray.length > 0 && !session.isPassiveUpdateEnabled(); + if (staleNoUpdate) + { + lockManager.unlock2(true, lockType, view, objectsToLock); + return new LockObjectsResult(false, false, false, requiredTimestamp, staleRevisionsArray, new CDOLockState[0]); + } + + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, viewedBranch, Operation.LOCK, cdoLockStates); + + boolean waitForUpdate = staleRevisionsArray.length > 0; + return new LockObjectsResult(true, false, waitForUpdate, requiredTimestamp, staleRevisionsArray, cdoLockStates); + } + + private void sendLockNotifications(IView view, CDOBranch viewedBranch, Operation operation, + CDOLockState[] cdoLockStates) + { + long timestamp = getTimeStamp(); + CDOLockChangeInfo lockChangeInfo = CDOLockUtil.createLockChangeInfo(timestamp, view, viewedBranch, operation, + cdoLockStates); + getSessionManager().sendLockNotification((InternalSession)view.getSession(), lockChangeInfo); + } + + // TODO (CD) This doesn't really belong here.. but getting it into CDOLockUtil isn't possible + private CDOLockState[] toCDOLockStates(List<LockState<Object, IView>> lockStates) + { + CDOLockState[] cdoLockStates = new CDOLockState[lockStates.size()]; + int i = 0; + + for (LockState<Object, ? extends CDOCommonView> lockState : lockStates) + { + cdoLockStates[i++] = CDOLockUtil.createLockState(lockState); + } + + return cdoLockStates; + } + + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + List<Object> revisionKeys = null; + if (objectIDs != null) + { + revisionKeys = new ArrayList<Object>(objectIDs.size()); + CDOBranch branch = view.getBranch(); + for (CDOID id : objectIDs) + { + Object key = supportingBranches ? CDOIDUtil.createIDAndBranch(id, branch) : id; + revisionKeys.add(key); + } + } + + List<LockState<Object, IView>> newLockStates = null; + if (lockType == null && revisionKeys == null) + { + newLockStates = lockManager.unlock2(true, view); + } + else + { + newLockStates = lockManager.unlock2(true, lockType, view, revisionKeys); + } + + CDOLockState[] cdoLockStates = toCDOLockStates(newLockStates); + sendLockNotifications(view, view.getBranch(), Operation.UNLOCK, cdoLockStates); + + return new UnlockObjectsResult(cdoLockStates); + } + @Override public String toString() { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java index 10868ef70f..3af601ddb4 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java @@ -19,6 +19,8 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; import org.eclipse.emf.cdo.common.id.CDOIDGenerator; import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; @@ -209,6 +211,11 @@ public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cd throw new UnsupportedOperationException(); } + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo) + { + // Do nothing + } + public void prefetchRevisions(CDOID id, int depth) { throw new UnsupportedOperationException(); @@ -279,6 +286,16 @@ public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cd throw new UnsupportedOperationException(); } + public boolean isLockNotificationEnabled() + { + return false; + } + + public void setLockNotificationEnabled(boolean enabled) + { + throw new UnsupportedOperationException(); + } + public CDOAdapterPolicy[] getChangeSubscriptionPolicies() { return ADAPTER_POLICIES; @@ -324,6 +341,11 @@ public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cd throw new UnsupportedOperationException(); } + public CDOLockState[] getLockStates(Collection<CDOID> ids) + { + throw new UnsupportedOperationException(); + } + /** * @author Eike Stepper */ @@ -784,6 +806,11 @@ public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cd throw new UnsupportedOperationException(); } + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo) + { + throw new UnsupportedOperationException(); + } + public void handleBranchNotification(InternalCDOBranch branch) { throw new UnsupportedOperationException(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java index 727e0fdb42..cf32de47d4 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java @@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; @@ -453,6 +454,23 @@ public class Session extends Container<IView> implements InternalSession } } + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception + { + if (protocol != null) + { + // If this session has one (or more) views configured for this branch, + // only then do we send the lockChangeInfo. + for (InternalView view : getViews()) + { + if (view.isLockNotificationEnabled() && view.getBranch().equals(lockChangeInfo.getBranch())) + { + protocol.sendLockNotification(lockChangeInfo); + break; + } + } + } + } + private boolean hasSubscription(CDOID id, InternalView[] views) { for (InternalView view : views) 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 e519b9d216..7869c798d1 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 @@ -14,6 +14,7 @@ package org.eclipse.emf.cdo.internal.server; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.emf.cdo.server.ISession; @@ -307,6 +308,23 @@ public class SessionManager extends Container<ISession> implements InternalSessi } } + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo) + { + for (InternalSession session : getSessions()) + { + // TODO Exclude the sender and notify locally there + + try + { + session.sendLockNotification(lockChangeInfo); + } + catch (Exception ex) + { + handleNotificationProblem(session, ex); + } + } + } + /** * @since 2.0 */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java index 376ef17ef8..e2290cee19 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java @@ -42,6 +42,7 @@ import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.emf.cdo.server.ContainmentCycleDetectedException; import org.eclipse.emf.cdo.server.IStoreAccessor; import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.server.StoreThreadLocal; import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; @@ -66,6 +67,7 @@ import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.StringUtil; import org.eclipse.net4j.util.collection.IndexedList; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; import org.eclipse.net4j.util.io.ExtendedDataInputStream; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; import org.eclipse.net4j.util.om.monitor.Monitor; @@ -147,6 +149,8 @@ public class TransactionCommitContext implements InternalCommitContext private List<CDOIDReference> xRefs; + private List<LockState<Object, IView>> postCommitLockStates; + private boolean ensuringReferentialIntegrity; private boolean autoReleaseLocksEnabled; @@ -487,6 +491,11 @@ public class TransactionCommitContext implements InternalCommitContext } } + public List<LockState<Object, IView>> getPostCommmitLockStates() + { + return postCommitLockStates; + } + private void handleException(Throwable ex) { try @@ -1046,7 +1055,7 @@ public class TransactionCommitContext implements InternalCommitContext if (isAutoReleaseLocksEnabled()) { - repository.getLockManager().unlock(transaction); + postCommitLockStates = repository.getLockManager().unlock2(true, transaction); } monitor.worked(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java index 5701fc342e..a81cbb8d19 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java @@ -22,6 +22,7 @@ import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.spi.server.InternalSession; import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.lifecycle.Lifecycle; import java.text.MessageFormat; @@ -37,7 +38,10 @@ public class View extends Lifecycle implements InternalView { private InternalSession session; - private int viewID; + private final int viewID; + + private final int sessionID; // Needed here so we can compute the hashCode even after session becomes null due to + // deactivation! private CDOBranchPoint branchPoint; @@ -47,6 +51,8 @@ public class View extends Lifecycle implements InternalView private Set<CDOID> changeSubscriptionIDs = new HashSet<CDOID>(); + private boolean lockNotificationsEnabled; + /** * @since 2.0 */ @@ -54,6 +60,7 @@ public class View extends Lifecycle implements InternalView { this.session = session; this.viewID = viewID; + sessionID = session.getSessionID(); repository = session.getManager().getRepository(); setBranchPoint(branchPoint); @@ -193,6 +200,12 @@ public class View extends Lifecycle implements InternalView } @Override + public int hashCode() + { + return ObjectUtil.hashCode(sessionID, viewID); + } + + @Override public String toString() { int sessionID = session == null ? 0 : session.getSessionID(); @@ -249,4 +262,14 @@ public class View extends Lifecycle implements InternalView throw new IllegalStateException("View closed"); //$NON-NLS-1$ } } + + public boolean isLockNotificationEnabled() + { + return lockNotificationsEnabled; + } + + public void setLockNotificationEnabled(boolean enable) + { + lockNotificationsEnabled = enable; + } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java index 86ef2bc33f..dd7471a941 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java @@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; @@ -140,6 +141,16 @@ public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessi return result; } + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) + { + throw new UnsupportedOperationException(); + } + + public void enableLockNotifications(int viewID, boolean enable) + { + throw new UnsupportedOperationException(); + } + public void disablePassiveUpdate() { // serverSessionProtocol.getSession().setPassiveUpdateEnabled(passiveUpdateEnabled); @@ -329,13 +340,39 @@ public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessi throw new UnsupportedOperationException(); } + @Deprecated public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException { throw new UnsupportedOperationException(); } - public void unlockObjects(CDOView view, Collection<? extends CDOObject> objects, LockType lockType) + /** + * @since 4.1 + */ + public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + throw new UnsupportedOperationException(); + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException + { + throw new UnsupportedOperationException(); + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType) { throw new UnsupportedOperationException(); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java index 8fd0ce555d..adfec3f101 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedServerSessionProtocol.java @@ -12,6 +12,7 @@ package org.eclipse.emf.cdo.internal.server.embedded; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; @@ -82,6 +83,12 @@ public class EmbeddedServerSessionProtocol extends Lifecycle implements ISession clientSession.handleCommitNotification(commitInfo); } + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) + { + EmbeddedClientSession clientSession = clientSessionProtocol.getSession(); + clientSession.handleLockNotification(lockChangeInfo); + } + public void sendRemoteSessionNotification(InternalSession sender, byte opcode) { throw new UnsupportedOperationException(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java index 3793ac14aa..1f3b13f3b4 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStore.java @@ -32,7 +32,7 @@ import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.util.CDOCommonUtil; import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.server.IStoreAccessor; -import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; import org.eclipse.emf.cdo.server.ITransaction; import org.eclipse.emf.cdo.server.IView; @@ -88,7 +88,7 @@ import java.util.Set; /** * @author Simon McDuff */ -public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, DurableLocking +public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, DurableLocking2 { public static final String TYPE = "mem"; //$NON-NLS-1$ @@ -740,12 +740,27 @@ public class MEMStore extends LongIDStore implements IMEMStore, BranchLoader, Du public synchronized LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, Map<CDOID, LockGrade> locks) { - String durableLockingID; + return createLockArea(null, userID, branchPoint, readOnly, locks); + } - do + public synchronized LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, + boolean readOnly, Map<CDOID, LockGrade> locks) + { + if (durableLockingID != null) { - durableLockingID = DurableLockArea.createDurableLockingID(); - } while (lockAreas.containsKey(durableLockingID)); + // If the caller is specifying the ID, make sure there is no area with this ID yet + if (lockAreas.containsKey(durableLockingID)) + { + throw new LockAreaAlreadyExistsException(durableLockingID); + } + } + else + { + do + { + durableLockingID = DurableLockArea.createDurableLockingID(); + } while (lockAreas.containsKey(durableLockingID)); + } LockArea area = new DurableLockArea(durableLockingID, userID, branchPoint, readOnly, locks); lockAreas.put(durableLockingID, area); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java index 2e071a7ecd..90974d1a21 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java @@ -28,7 +28,7 @@ import org.eclipse.emf.cdo.common.util.CDOQueryInfo; import org.eclipse.emf.cdo.server.IQueryContext; import org.eclipse.emf.cdo.server.IQueryHandler; import org.eclipse.emf.cdo.server.ISession; -import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; import org.eclipse.emf.cdo.server.IStoreAccessor.Raw; import org.eclipse.emf.cdo.server.ITransaction; import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; @@ -62,7 +62,7 @@ import java.util.Set; /** * @author Simon McDuff */ -public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking +public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking2 { private final IQueryHandler testQueryHandler = new IQueryHandler() { @@ -408,6 +408,12 @@ public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, Durabl return getStore().createLockArea(userID, branchPoint, readOnly, locks); } + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return getStore().createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException { return getStore().getLockArea(durableLockingID); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java index 712438d42e..9f424cbd0b 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/syncing/SynchronizableRepository.java @@ -25,6 +25,7 @@ import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOException; import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl; import org.eclipse.emf.cdo.internal.server.Repository; import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; @@ -43,13 +44,20 @@ import org.eclipse.emf.cdo.spi.server.InternalSessionManager; import org.eclipse.emf.cdo.spi.server.InternalStore; import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository; import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; import org.eclipse.net4j.util.om.monitor.Monitor; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.transaction.TransactionException; import org.eclipse.emf.spi.cdo.CDOSessionProtocol; import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; +import org.eclipse.emf.spi.cdo.InternalCDOSession; import java.io.IOException; import java.util.Arrays; @@ -420,6 +428,99 @@ public abstract class SynchronizableRepository extends Repository.Default implem setState(INITIAL); } + @Override + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, long timeout) + { + CheckUtil.checkState(view.getBranch().equals(viewedBranch), + "Client view's branch and server view's branch are different."); + + if (view.getBranch().isLocal()) + { + return super.lock(view, lockType, revisionKeys, viewedBranch, timeout); + } + + if (getState() != ONLINE) + { + throw new CDOException("Cannot lock in a non-local branch when clone is not connected to master"); + } + + return lockThrough(true, view, lockType, revisionKeys, viewedBranch, timeout); + } + + private LockObjectsResult lockThrough(boolean explicit, InternalView view, LockType lockType, + List<CDORevisionKey> revisionKeys, CDOBranch viewedBranch, long timeout) + { + // Delegate locking to the master + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + try + { + String lockAreaID = view.getDurableLockingID(); + if (lockAreaID == null) + { + throw new IllegalStateException("Durable locking is not enabled."); + } + + LockObjectsResult masterLockingResult = sessionProtocol.delegateLockObjects(lockAreaID, revisionKeys, + viewedBranch, lockType, timeout); + if (!masterLockingResult.isSuccessful()) + { + return masterLockingResult; + } + + if (masterLockingResult.isWaitForUpdate()) + { + if (!getSynchronizer().getRemoteSession().options().isPassiveUpdateEnabled()) + { + throw new AssertionError( + "Master lock result requires clone to wait, but clone does not have passiveUpdates enabled."); + } + + long requiredTimestamp = masterLockingResult.getRequiredTimestamp(); + remoteSession.waitForUpdate(requiredTimestamp); + } + + return super.lock(view, lockType, revisionKeys, viewedBranch, timeout); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + @Override + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + if (view.getBranch().isLocal()) + { + return super.unlock(view, lockType, objectIDs); + } + + if (getState() != ONLINE) + { + throw new CDOException("Cannot unlock in a non-local branch when clone is not connected to master"); + } + + return unlockThrough(view, lockType, objectIDs); + } + + private UnlockObjectsResult unlockThrough(InternalView view, LockType lockType, List<CDOID> objectIDs) + { + // Delegate unlocking to the master + InternalCDOSession remoteSession = getSynchronizer().getRemoteSession(); + CDOSessionProtocol sessionProtocol = remoteSession.getSessionProtocol(); + + String lockAreaID = view.getDurableLockingID(); + if (lockAreaID == null) + { + throw new IllegalStateException("Durable locking is not enabled."); + } + + sessionProtocol.delegateUnlockObjects(lockAreaID, objectIDs, lockType); + return super.unlock(view, lockType, objectIDs); + } + /** * @author Eike Stepper */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java index d93c7cad2b..d9da5d9cc5 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -42,6 +42,7 @@ import org.eclipse.emf.cdo.spi.server.InternalCommitContext; import org.eclipse.emf.cdo.spi.server.InternalSession; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; import org.eclipse.net4j.util.io.ExtendedDataInputStream; import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.om.monitor.OMMonitor; @@ -393,6 +394,11 @@ public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, Com * @since 4.0 */ public List<CDOIDReference> getXRefs(); + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> getPostCommmitLockStates(); } /** @@ -700,4 +706,14 @@ public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, Com public void unlock(String durableLockingID); } + + /** + * @author Eike Stepper + * @since 4.1 + */ + public interface DurableLocking2 extends DurableLocking + { + LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks); + } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java index 2edafbe276..d8e378f566 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/ISessionProtocol.java @@ -12,6 +12,7 @@ package org.eclipse.emf.cdo.spi.server; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.protocol.CDOProtocol; import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; import org.eclipse.emf.cdo.spi.common.CDOAuthenticationResult; @@ -43,4 +44,9 @@ public interface ISessionProtocol extends CDOProtocol public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception; public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception; + + /** + * @since 4.1 + */ + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception; } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java index e7bd2e6226..d96cc595bf 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockManager.java @@ -17,9 +17,11 @@ import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.server.IView; -import org.eclipse.net4j.util.concurrent.IRWLockManager; +import org.eclipse.net4j.util.concurrent.IRWOLockManager; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -31,7 +33,7 @@ import java.util.Map; * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ -public interface InternalLockManager extends IRWLockManager<Object, IView>, IDurableLockingManager +public interface InternalLockManager extends IRWOLockManager<Object, IView>, IDurableLockingManager { public InternalRepository getRepository(); @@ -60,32 +62,67 @@ public interface InternalLockManager extends IRWLockManager<Object, IView>, IDur /** * @since 4.0 */ + @Deprecated public void lock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException; /** + * @since 4.1 + */ + public List<LockState<Object, IView>> lock2(boolean explicit, LockType type, IView context, + Collection<? extends Object> objectsToLock, long timeout) throws InterruptedException; + + /** * Attempts to release for a given locktype, view and objects. * * @throws IllegalMonitorStateException * Unlocking objects without lock. * @since 4.0 */ + @Deprecated public void unlock(boolean explicit, LockType type, IView context, Collection<? extends Object> objectsToUnlock); /** + * @since 4.1 + */ + public List<LockState<Object, IView>> unlock2(boolean explicit, LockType type, IView context, + Collection<? extends Object> objectsToUnlock); + + /** * Attempts to release all locks(read and write) for a given view. * * @since 4.0 */ + @Deprecated public void unlock(boolean explicit, IView context); /** + * @since 4.1 + */ + public List<LockState<Object, IView>> unlock2(boolean explicit, IView context); + + /** * @since 4.0 */ public LockArea createLockArea(InternalView view); /** + * @since 4.1 + */ + public LockArea createLockArea(InternalView view, String lockAreaID); + + /** * @since 4.0 */ public IView openView(ISession session, int viewID, boolean readOnly, String durableLockingID); + + /** + * @since 4.1 + */ + public LockGrade getLockGrade(Object key); + + /** + * @since 4.1 + */ + public LockState<Object, IView> getLockState(Object key); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockingManagerContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockingManagerContext.java new file mode 100644 index 0000000000..00782c3f19 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalLockingManagerContext.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.spi.server; + +/** + * @author Caspar De Groot + * @since 4.1 + */ +public interface InternalLockingManagerContext +{ + +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java index 3e7306d3b6..732f716931 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java @@ -19,6 +19,7 @@ import org.eclipse.emf.cdo.common.lob.CDOLobHandler; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.server.IQueryHandlerProvider; import org.eclipse.emf.cdo.server.IRepository; import org.eclipse.emf.cdo.server.IStoreAccessor; @@ -37,10 +38,13 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager.RevisionLoader; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; import java.io.IOException; import java.io.OutputStream; @@ -213,4 +217,15 @@ public interface InternalRepository extends IRepository, PackageProcessor, Packa * @since 4.0 */ public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp); + + /** + * @since 4.1 + */ + public LockObjectsResult lock(InternalView view, LockType lockType, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, long timeout); + + /** + * @since 4.1 + */ + public UnlockObjectsResult unlock(InternalView view, LockType lockType, List<CDOID> objectIDs); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java index 47940aef62..e06734b554 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSession.java @@ -16,6 +16,7 @@ import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; @@ -65,4 +66,9 @@ public interface InternalSession extends ISession, CDOIDProvider, CDOCommonSessi public void sendRemoteSessionNotification(InternalSession sender, byte opcode) throws Exception; public void sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message) throws Exception; + + /** + * @since 4.1 + */ + public void sendLockNotification(CDOLockChangeInfo lockChangeInfo) throws Exception; } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java index 7b4679285f..f82d68a887 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalSessionManager.java @@ -12,6 +12,7 @@ package org.eclipse.emf.cdo.spi.server; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.server.ISessionManager; import org.eclipse.emf.cdo.session.remote.CDORemoteSessionMessage; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; @@ -53,6 +54,11 @@ public interface InternalSessionManager extends ISessionManager public void sendCommitNotification(InternalSession sender, CDOCommitInfo commitInfo); + /** + * @since 4.1 + */ + public void sendLockNotification(InternalSession sender, CDOLockChangeInfo lockChangeInfo); + public void sendRemoteSessionNotification(InternalSession sender, byte opcode); public List<Integer> sendRemoteMessageNotification(InternalSession sender, CDORemoteSessionMessage message, diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java index 8adca8fe50..7440be841f 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java @@ -53,4 +53,14 @@ public interface InternalView extends IView, ILifecycle public void clearChangeSubscription(); public void doClose(); + + /** + * @since 4.1 + */ + public boolean isLockNotificationEnabled(); + + /** + * @since 4.1 + */ + public void setLockNotificationEnabled(boolean enable); } diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Offline.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Offline.java index 80d8ba1087..26d0419211 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Offline.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Offline.java @@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.tests.offline.Bugzilla_328352_Test; import org.eclipse.emf.cdo.tests.offline.Bugzilla_329014_Test; import org.eclipse.emf.cdo.tests.offline.FailoverTest; import org.eclipse.emf.cdo.tests.offline.OfflineDelayedTest; +import org.eclipse.emf.cdo.tests.offline.OfflineLockingTest; import org.eclipse.emf.cdo.tests.offline.OfflineRawTest; import org.eclipse.emf.cdo.tests.offline.OfflineTest; @@ -51,6 +52,7 @@ public class AllTestsDBH2Offline extends DBConfigs testClasses.add(OfflineTest.class); testClasses.add(OfflineRawTest.class); testClasses.add(OfflineDelayedTest.class); + testClasses.add(OfflineLockingTest.class); testClasses.add(Bugzilla_329014_Test.class); testClasses.add(Bugzilla_328352_Test.class); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java index 7b49bbe8b8..a488d28d6e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java @@ -74,6 +74,7 @@ public abstract class AllConfigs extends ConfigTestSuite testClasses.add(LockingManagerRestartTransactionTest.class); testClasses.add(LockingManagerRestartSessionTest.class); testClasses.add(LockingManagerRestartRepositoryTest.class); + testClasses.add(LockingNotificationsTest.class); testClasses.add(MultiValuedOfAttributeTest.class); testClasses.add(MapTest.class); testClasses.add(FeatureMapTest.class); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsMEMOffline.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsMEMOffline.java index 72e59220cb..c67d939679 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsMEMOffline.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTestsMEMOffline.java @@ -14,6 +14,7 @@ import org.eclipse.emf.cdo.tests.config.IScenario; import org.eclipse.emf.cdo.tests.config.impl.ConfigTest; import org.eclipse.emf.cdo.tests.offline.FailoverTest; import org.eclipse.emf.cdo.tests.offline.OfflineDelayed2Test; +import org.eclipse.emf.cdo.tests.offline.OfflineLockingTest; import org.eclipse.emf.cdo.tests.offline.OfflineTest; import java.util.List; @@ -43,6 +44,7 @@ public class AllTestsMEMOffline extends AllConfigs // MEM does not support raw replication // testClasses.add(OfflineRawTest.class); + testClasses.add(OfflineLockingTest.class); testClasses.add(OfflineTest.class); testClasses.add(OfflineDelayed2Test.class); testClasses.add(FailoverTest.class); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingNotificationsTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingNotificationsTest.java new file mode 100644 index 0000000000..f9b8f6e677 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/LockingNotificationsTest.java @@ -0,0 +1,341 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.tests; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.util.CommitException; +import org.eclipse.emf.cdo.view.CDOLocksChangedEvent; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.net4j.util.event.IEvent; +import org.eclipse.net4j.util.event.IListener; + +import java.util.LinkedList; +import java.util.List; + +/** + * @author Caspar De Groot + */ +public class LockingNotificationsTest extends AbstractLockingTest +{ + private CDOView openViewWithLockNotifications(CDOSession session, CDOBranch branch) + { + CDOView view = branch != null ? session.openView(branch) : session.openView(); + view.options().setLockNotificationEnabled(true); + return view; + } + + public void testSameBranchDifferentSession() throws CommitException + { + CDOSession session1 = openSession(); + CDOSession session2 = openSession(); + CDOView controlView = openViewWithLockNotifications(session2, null); + test(session1, controlView, true); + session1.close(); + session2.close(); + } + + public void testSameBranchSameSession() throws CommitException + { + CDOSession session1 = openSession(); + CDOView controlView = openViewWithLockNotifications(session1, null); + test(session1, controlView, true); + session1.close(); + } + + public void testDifferentBranchDifferentSession() throws CommitException + { + skipUnlessBranching(); + + CDOSession session1 = openSession(); + CDOBranch subBranch = session1.getBranchManager().getMainBranch().createBranch("sub1"); + CDOSession session2 = openSession(); + CDOView controlView = openViewWithLockNotifications(session2, subBranch); + test(session1, controlView, false); + session1.close(); + session2.close(); + } + + public void testDifferentBranchSameSession() throws CommitException + { + skipUnlessBranching(); + + CDOSession session1 = openSession(); + CDOBranch subBranch = session1.getBranchManager().getMainBranch().createBranch("sub2"); + CDOView controlView = openViewWithLockNotifications(session1, subBranch); + test(session1, controlView, false); + session1.close(); + } + + private void test(CDOSession session1, CDOView controlView, boolean mustReceiveNotifications) throws CommitException + { + CDOTransaction tx1 = session1.openTransaction(); + CDOResource res1 = tx1.getOrCreateResource(getResourcePath("r1")); + LockEventListener listener1 = new LockEventListener(); + tx1.addListener(listener1); + res1.getContents().clear(); + Company company1 = getModel1Factory().createCompany(); + res1.getContents().add(company1); + tx1.commit(); + + LockEventListener listener2 = new LockEventListener(); + controlView.addListener(listener2); + + CDOObject cdoCompany1 = CDOUtil.getCDOObject(company1); + + /* Test write lock */ + + cdoCompany1.cdoWriteLock().lock(); + if (mustReceiveNotifications) + { + listener2.waitFor(1); + assertEquals(1, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(0); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertLockedObject(cdoCompany1, lockStates[0].getLockedObject()); + assertLockOwner(tx1, lockStates[0].getWriteLockOwner()); + } + + cdoCompany1.cdoWriteLock().unlock(); + if (mustReceiveNotifications) + { + listener2.waitFor(2); + + assertEquals(2, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(1); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertLockedObject(cdoCompany1, lockStates[0].getLockedObject()); + assertNull(lockStates[0].getWriteLockOwner()); + } + + /* Test read lock */ + + cdoCompany1.cdoReadLock().lock(); + if (mustReceiveNotifications) + { + listener2.waitFor(3); + assertEquals(3, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(2); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertLockedObject(cdoCompany1, lockStates[0].getLockedObject()); + assertEquals(1, lockStates[0].getReadLockOwners().size()); + CDOLockOwner tx1Lo = CDOLockUtil.createLockOwner(tx1); + assertEquals(true, lockStates[0].getReadLockOwners().contains(tx1Lo)); + } + + cdoCompany1.cdoReadLock().unlock(); + if (mustReceiveNotifications) + { + listener2.waitFor(4); + + assertEquals(4, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(3); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertEquals(0, lockStates[0].getReadLockOwners().size()); + } + + /* Test write option */ + + cdoCompany1.cdoWriteOption().lock(); + if (mustReceiveNotifications) + { + listener2.waitFor(5); + assertEquals(5, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(4); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertLockedObject(cdoCompany1, lockStates[0].getLockedObject()); + assertLockOwner(tx1, lockStates[0].getWriteOptionOwner()); + } + + cdoCompany1.cdoWriteOption().unlock(); + if (mustReceiveNotifications) + { + listener2.waitFor(6); + + assertEquals(6, listener2.getLockEvents().size()); + + CDOLocksChangedEvent event = listener2.getLockEvents().get(5); + assertLockOwner(tx1, event.getLockOwner()); + + CDOLockState[] lockStates = event.getLockStates(); + assertEquals(1, lockStates.length); + assertLockedObject(cdoCompany1, lockStates[0].getLockedObject()); + assertNull(lockStates[0].getWriteOptionOwner()); + } + + assertEquals(0, listener1.getLockEvents().size()); + + if (!mustReceiveNotifications) + { + assertEquals(0, listener2.getLockEvents().size()); + } + } + + private void assertLockedObject(CDOObject obj, Object lockedObject) + { + if (lockedObject instanceof CDOIDAndBranch) + { + CDOIDAndBranch idAndBranch = CDOIDUtil.createIDAndBranch(obj.cdoID(), obj.cdoView().getBranch()); + assertEquals(idAndBranch, lockedObject); + } + else if (lockedObject instanceof CDOID) + { + assertEquals(obj.cdoID(), lockedObject); + } + } + + private void assertLockOwner(CDOView view, CDOLockOwner lockOwner) + { + CDOLockOwner lo = CDOLockUtil.createLockOwner(view); + assertEquals(lo, lockOwner); + } + + /** + * @author Caspar De Groot + */ + private final class LockEventListener implements IListener + { + private List<CDOLocksChangedEvent> lockEvents = new LinkedList<CDOLocksChangedEvent>(); + + public synchronized void notifyEvent(IEvent event) + { + if (event instanceof CDOLocksChangedEvent) + { + lockEvents.add((CDOLocksChangedEvent)event); + notify(); + } + } + + public List<CDOLocksChangedEvent> getLockEvents() + { + return lockEvents; + } + + public synchronized void waitFor(int n) + { + long timeout = 2000; + long t = 0; + while (lockEvents.size() < n) + { + if (timeout <= 0) + { + fail("Timed out"); + } + try + { + t = System.currentTimeMillis(); + wait(timeout); + } + catch (InterruptedException ex) + { + } + timeout -= System.currentTimeMillis() - t; + } + } + } + + public void testEnableDisableNotifications() throws CommitException + { + CDOSession session1 = openSession(); + CDOSession session2 = openSession(); + CDOView controlView = session2.openView(); + test(session1, controlView, false); + + controlView.options().setLockNotificationEnabled(true); + test(session1, controlView, true); + + controlView.options().setLockNotificationEnabled(false); + test(session1, controlView, false); + + session1.close(); + session2.close(); + } + + public void testLockStateHeldByDurableView() throws CommitException + { + { + CDOSession session1 = openSession(); + CDOTransaction tx1 = session1.openTransaction(); + tx1.enableDurableLocking(true); + CDOResource res1 = tx1.createResource(getResourcePath("r1")); + Company company1 = getModel1Factory().createCompany(); + res1.getContents().add(company1); + tx1.commit(); + + CDOUtil.getCDOObject(company1).cdoWriteLock().lock(); + tx1.close(); + session1.close(); + } + + CDOSession session2 = openSession(); + CDOView controlView = session2.openView(); + CDOResource resource = controlView.getResource(getResourcePath("r1")); + Company company1 = (Company)resource.getContents().get(0); + CDOObject cdoObj = CDOUtil.getCDOObject(company1); + assertEquals(true, cdoObj.cdoWriteLock().isLockedByOthers()); + assertSame(CDOLockOwner.UNKNOWN, cdoObj.cdoLockState().getWriteLockOwner()); + session2.close(); + } + + public void testLockStateNewAndTransient() throws CommitException + { + Company company1 = getModel1Factory().createCompany(); + CDOObject cdoObj = CDOUtil.getCDOObject(company1); + assertTransient(cdoObj); + assertNull(cdoObj.cdoLockState()); + + CDOSession session1 = openSession(); + CDOTransaction tx1 = session1.openTransaction(); + CDOResource res1 = tx1.createResource(getResourcePath("r1")); + res1.getContents().add(company1); + assertNew(cdoObj, tx1); + assertNull(cdoObj.cdoLockState()); + + tx1.commit(); + assertClean(cdoObj, tx1); + assertNotNull(cdoObj.cdoLockState()); + + res1.getContents().add(company1); + tx1.commit(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/offline/OfflineLockingTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/offline/OfflineLockingTest.java new file mode 100644 index 0000000000..250fa7cd3b --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/offline/OfflineLockingTest.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.tests.offline; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.tests.AbstractSyncingTest; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.view.CDOView; + +/** + * @author Caspar De Groot + */ +public class OfflineLockingTest extends AbstractSyncingTest +{ + public void testLockAndUnlockThrough() throws Exception + { + // InternalRepository clone = getRepository(); + // InternalRepository master = getRepository(clone.getName() + "_master"); + + CDOSession masterSession = openSession(getRepository().getName() + "_master"); + // CDOTransaction masterTx = masterSession.openTransaction(); + + CDOSession cloneSession = openSession(); + waitForOnline(cloneSession.getRepositoryInfo()); + + CDOTransaction cloneTx = cloneSession.openTransaction(); + cloneTx.enableDurableLocking(true); + + CDOResource res = cloneTx.createResource(getResourcePath("test")); + Company company = getModel1Factory().createCompany(); + res.getContents().add(company); + cloneTx.commit(); + + CDOObject cdoCompany = CDOUtil.getCDOObject(company); + cdoCompany.cdoWriteLock().lock(); + + CDOView masterView = masterSession.openView(); + CDOObject cdoCompanyOnMaster = masterView.getObject(cdoCompany.cdoID()); + assertEquals(true, cdoCompanyOnMaster.cdoWriteLock().isLockedByOthers()); + + // int viewID = cloneTx.getViewID(); + // master.getLockManager().hasLock(LockType.WRITE, context, objectToLock); + + cdoCompany.cdoWriteLock().unlock(); + assertEquals(false, cdoCompanyOnMaster.cdoWriteLock().isLockedByOthers()); + + cloneSession.close(); + masterSession.close(); + } + + public void testMasterLocks_ArrivalInClone() throws Exception + { + + } +} diff --git a/plugins/org.eclipse.emf.cdo/.settings/.api_filters b/plugins/org.eclipse.emf.cdo/.settings/.api_filters index 43d0cb251c..c9ee6895d7 100644 --- a/plugins/org.eclipse.emf.cdo/.settings/.api_filters +++ b/plugins/org.eclipse.emf.cdo/.settings/.api_filters @@ -4,6 +4,12 @@ <filter id="403804204">
<message_arguments>
<message_argument value="org.eclipse.emf.cdo.CDOObject"/>
+ <message_argument value="cdoLockState()"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.emf.cdo.CDOObject"/>
<message_argument value="cdoWriteOption()"/>
</message_arguments>
</filter>
@@ -85,6 +91,20 @@ </message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/emf/cdo/view/CDOLocksChangedEvent.java" type="org.eclipse.emf.cdo.view.CDOLocksChangedEvent">
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOBranchPoint"/>
+ <message_argument value="CDOLocksChangedEvent"/>
+ </message_arguments>
+ </filter>
+ <filter id="571473929">
+ <message_arguments>
+ <message_argument value="CDOLockChangeInfo"/>
+ <message_argument value="CDOLocksChangedEvent"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/emf/cdo/view/CDOQuery.java" type="org.eclipse.emf.cdo.view.CDOQuery">
<filter id="571473929">
<message_arguments>
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 24e65bfe08..c08fc07adf 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 @@ -13,6 +13,7 @@ package org.eclipse.emf.cdo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDTemp; import org.eclipse.emf.cdo.common.id.CDOWithID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionManager; import org.eclipse.emf.cdo.eresource.CDOResource; @@ -141,6 +142,13 @@ public interface CDOObject extends EObject, CDOWithID public CDOLock cdoWriteOption(); /** + * Returns the {@link CDOLockState} of this object. + * + * @since 4.1 + */ + public CDOLockState cdoLockState(); + + /** * Ensures that the revisions of the contained objects up to the given depth are in the local * {@link CDORevisionManager revision cache}. Subsequent access to the respective contained objects will not lead to * server round-trips after calling this method. diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOLocksChangedEvent.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOLocksChangedEvent.java new file mode 100644 index 0000000000..904dabbaaf --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOLocksChangedEvent.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.emf.cdo.view; + +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; + +/** + * A {@link CDOViewEvent view event} fired when lock notifications are being received from a repository. + * {@link CDOView.Options#setLockNotificationEnabled(boolean)} must be enabled for this event to be fired. + * + * @author Caspar De Groot + * @since 4.1 + */ +public interface CDOLocksChangedEvent extends CDOViewEvent, CDOLockChangeInfo +{ +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java index bfe7d060d3..4d74a8f3c2 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java @@ -21,6 +21,7 @@ import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.util.CDOException; import org.eclipse.emf.cdo.eresource.CDOResource; @@ -34,6 +35,7 @@ import org.eclipse.emf.cdo.util.ReadOnlyException; import org.eclipse.net4j.util.collection.CloseableIterator; import org.eclipse.net4j.util.concurrent.IRWLockManager; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.event.INotifier; import org.eclipse.net4j.util.options.IOptions; import org.eclipse.net4j.util.options.IOptionsContainer; @@ -466,6 +468,27 @@ public interface CDOView extends CDOCommonView, CDOUpdatable, INotifier, IOption public void setInvalidationNotificationEnabled(boolean enabled); /** + * Returns <code>true</code> if this view will notify its {@link IListener listeners} about changes to the + * {@link CDOLockState lock states} of the objects in this view (due to lock operations in <i>other</i> views), + * <code>false</code> otherwise. + * + * @see CDOLocksChangedEvent + * @see CDOLockState + * @since 4.1 + */ + public boolean isLockNotificationEnabled(); + + /** + * Specifies whether this view will notify its {@link IListener listeners} about changes to the {@link CDOLockState + * lock states} of the objects in this view (due to lock operations in <i>other</i> views), or not. + * + * @see CDOLocksChangedEvent + * @see CDOLockState + * @since 4.1 + */ + public void setLockNotificationEnabled(boolean enabled); + + /** * Returns the current set of {@link CDOAdapterPolicy change subscription policies}. * * @return The current set of change subscription policies, never <code>null</code>. @@ -646,6 +669,14 @@ public interface CDOView extends CDOCommonView, CDOUpdatable, INotifier, IOption } /** + * @author Caspar De Groot + * @since 4.1 + */ + public interface LockNotificationEvent extends IOptionsEvent + { + } + + /** * An {@link IOptionsEvent options event} fired from view {@link CDOView#options() options} when the * {@link Options#setRevisionPrefetchingPolicy(CDORevisionPrefetchingPolicy) revision prefetching policy} option has * changed. 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 e2e2aa0924..ffe5f921ce 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 @@ -15,6 +15,7 @@ import org.eclipse.emf.cdo.CDOLock; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.EMFUtil; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.eresource.CDOResource; @@ -63,6 +64,7 @@ import org.eclipse.emf.spi.cdo.InternalCDOObject; import org.eclipse.emf.spi.cdo.InternalCDOView; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -84,6 +86,8 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec private InternalCDORevision revision; + private CDOLockState lockState; + /** * CDO uses this list instead of eSettings for transient objects. EMF uses 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 @@ -184,17 +188,7 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec */ public CDOLock cdoReadLock() { - if (FSMUtil.isTransient(this)) - { - throw new IllegalStateException("Call CDOView.lockObjects() for transient object " + this); - } - - if (FSMUtil.isNew(this)) - { - return CDOLockImpl.NOOP; - } - - return new CDOLockImpl(this, LockType.READ); + return createCDOLock(LockType.READ); } /** @@ -202,6 +196,19 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec */ public CDOLock cdoWriteLock() { + return createCDOLock(LockType.WRITE); + } + + /** + * @since 4.1 + */ + public CDOLock cdoWriteOption() + { + return createCDOLock(LockType.OPTION); + } + + private CDOLock createCDOLock(LockType type) + { if (FSMUtil.isTransient(this)) { throw new IllegalStateException("Call CDOView.lockObjects() for transient object " + this); @@ -212,25 +219,31 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec return CDOLockImpl.NOOP; } - return new CDOLockImpl(this, LockType.WRITE); + return new CDOLockImpl(this, type); } /** * @since 4.1 */ - public CDOLock cdoWriteOption() + public synchronized CDOLockState cdoLockState() { - if (FSMUtil.isTransient(this)) + if (lockState == null) { - throw new IllegalStateException("Call CDOView.lockObjects() for transient object " + this); + if (!FSMUtil.isTransient(this) && !FSMUtil.isNew(this)) + { + lockState = view.getLockStates(Collections.singletonList(id))[0]; + } } - if (FSMUtil.isNew(this)) - { - return CDOLockImpl.NOOP; - } + return lockState; + } - return new CDOLockImpl(this, LockType.OPTION); + /** + * @since 4.1 + */ + public synchronized void cdoInternalSetLockState(CDOLockState lockState) + { + this.lockState = lockState; } public void cdoInternalSetID(CDOID id) diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java index 3678c01d21..e0be18394d 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java @@ -14,6 +14,7 @@ package org.eclipse.emf.internal.cdo.object; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; import org.eclipse.emf.cdo.common.model.CDOType; @@ -57,6 +58,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -95,6 +97,8 @@ public abstract class CDOLegacyWrapper extends CDOObjectWrapper protected InternalCDORevision revision; + protected CDOLockState lockState; + /** * It could happen that while <i>revisionToInstance()</i> is executed externally the <i>internalPostLoad()</i> method * will be called. This happens for example if <i>internalPostInvalidate()</i> is called. The leads to another @@ -121,6 +125,24 @@ public abstract class CDOLegacyWrapper extends CDOObjectWrapper return revision; } + public synchronized CDOLockState cdoLockState() + { + if (lockState == null) + { + if (!FSMUtil.isTransient(this) && !FSMUtil.isNew(this)) + { + lockState = view.getLockStates(Collections.singletonList(id))[0]; + } + } + + return lockState; + } + + public synchronized void cdoInternalSetLockState(CDOLockState lockState) + { + this.lockState = lockState; + } + @Override public CDOResourceImpl cdoResource() { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLockImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLockImpl.java index 851c0a10cf..d161642a22 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLockImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLockImpl.java @@ -12,6 +12,8 @@ package org.eclipse.emf.internal.cdo.object; import org.eclipse.emf.cdo.CDOLock; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; import org.eclipse.emf.cdo.util.LockTimeoutException; import org.eclipse.net4j.util.WrappedException; @@ -33,14 +35,17 @@ public class CDOLockImpl implements CDOLock { public static final CDOLock NOOP = new NOOPLockImpl(); - private InternalCDOObject object; + private final InternalCDOObject object; - private LockType type; + private final LockType type; + + private final CDOLockOwner owner; public CDOLockImpl(InternalCDOObject object, LockType type) { this.object = object; this.type = type; + owner = CDOLockUtil.createLockOwner(object.cdoView()); } public LockType getType() @@ -50,7 +55,7 @@ public class CDOLockImpl implements CDOLock public boolean isLocked() { - return object.cdoView().isObjectLocked(object, type, false); + return object.cdoLockState().isLocked(type, owner, false); } /** @@ -58,7 +63,7 @@ public class CDOLockImpl implements CDOLock */ public boolean isLockedByOthers() { - return object.cdoView().isObjectLocked(object, type, true); + return object.cdoLockState().isLocked(type, owner, true); } public void lock() diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java index 4a39ec7d44..293f899465 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java @@ -27,6 +27,7 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDGenerator; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.model.EMFUtil; import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; @@ -89,6 +90,7 @@ import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.collection.Pair; import org.eclipse.net4j.util.concurrent.IRWLockManager; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.IRWOLockManager; import org.eclipse.net4j.util.concurrent.RWOLockManager; import org.eclipse.net4j.util.event.Event; import org.eclipse.net4j.util.event.EventUtil; @@ -183,7 +185,7 @@ public abstract class CDOSessionImpl extends CDOTransactionContainerImpl impleme } }; - private IRWLockManager<CDOSessionImpl, Object> lockManager = new RWOLockManager<CDOSessionImpl, Object>(); + private IRWOLockManager<CDOSessionImpl, Object> lockManager = new RWOLockManager<CDOSessionImpl, Object>(); @ExcludeFromDump private Set<CDOSessionImpl> singletonCollection = Collections.singleton(this); @@ -715,17 +717,48 @@ public abstract class CDOSessionImpl extends CDOTransactionContainerImpl impleme public boolean waitForUpdate(long updateTime, long timeoutMillis) { long end = timeoutMillis == NO_TIMEOUT ? Long.MAX_VALUE : System.currentTimeMillis() + timeoutMillis; - for (CDOView view : getViews()) + InternalCDOView views[] = getViews(); + if (views.length > 0) { - long viewTimeoutMillis = timeoutMillis == NO_TIMEOUT ? NO_TIMEOUT : end - System.currentTimeMillis(); - boolean ok = view.waitForUpdate(updateTime, viewTimeoutMillis); - if (!ok) + for (CDOView view : views) { - return false; + long viewTimeoutMillis = timeoutMillis == NO_TIMEOUT ? NO_TIMEOUT : end - System.currentTimeMillis(); + boolean ok = view.waitForUpdate(updateTime, viewTimeoutMillis); + if (!ok) + { + return false; + } } + + return true; } - return true; + // Session without views + for (;;) + { + synchronized (lastUpdateTimeLock) + { + if (lastUpdateTime >= updateTime) + { + return true; + } + + long now = System.currentTimeMillis(); + if (now >= end) + { + return false; + } + + try + { + lastUpdateTimeLock.wait(end - now); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + } } /** @@ -803,6 +836,14 @@ public abstract class CDOSessionImpl extends CDOTransactionContainerImpl impleme } } + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo) + { + for (InternalCDOView view : getViews()) + { + view.handleLockNotification(lockChangeInfo); + } + } + private void registerPackageUnits(List<CDOPackageUnit> packageUnits) { InternalCDOPackageRegistry packageRegistry = getPackageRegistry(); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java index 875a77172d..c88715ae53 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java @@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; @@ -384,6 +385,38 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) + { + int attempt = 0; + for (;;) + { + try + { + return delegate.getLockStates(viewID, ids); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + + public void enableLockNotifications(int viewID, boolean enable) + { + int attempt = 0; + for (;;) + { + try + { + delegate.enableLockNotifications(viewID, enable); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + public boolean isObjectLocked(CDOView view, CDOObject object, LockType lockType, boolean byOthers) { int attempt = 0; @@ -579,6 +612,7 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } + @Deprecated public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException { @@ -596,6 +630,60 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } + /** + * @since 4.1 + */ + public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException + + { + int attempt = 0; + for (;;) + { + try + { + return delegate.lockObjects2(revisionKeys, viewID, viewedBranch, lockType, timeout); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + + public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException + { + int attempt = 0; + for (;;) + { + try + { + return delegate.delegateLockObjects(lockAreaID, revisionKeys, viewedBranch, lockType, timeout); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType) + { + int attempt = 0; + for (;;) + { + try + { + return delegate.delegateUnlockObjects(lockAreaID, objectIDs, lockType); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + public void query(CDOView view, AbstractQueryIterator<?> queryResult) { int attempt = 0; @@ -665,14 +753,14 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } - public void unlockObjects(CDOView view, Collection<? extends CDOObject> objects, LockType lockType) + public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType) { int attempt = 0; for (;;) { try { - delegate.unlockObjects(view, objects, lockType); + delegate.unlockObjects(view, objectIDs, lockType); return; } catch (Exception ex) @@ -682,6 +770,22 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } + public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType) + { + int attempt = 0; + for (;;) + { + try + { + return delegate.unlockObjects2(view, objectIDs, lockType); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + public List<CDORemoteSession> getRemoteSessions(InternalCDORemoteSessionManager manager, boolean subscribe) { int attempt = 0; diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java index 0968432db1..2f81f19424 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java @@ -34,6 +34,7 @@ import org.eclipse.emf.cdo.common.id.CDOIDTemp; import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; @@ -2625,6 +2626,12 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.COMMITTED, idMappings), listeners); } + + CDOLockState[] newLockStates = result.getNewLockStates(); + if (newLockStates != null) + { + updateLockStates(newLockStates); + } } catch (RuntimeException ex) { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java index 5f4d77d889..7e69345d50 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java @@ -18,6 +18,11 @@ import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockOwner; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; @@ -36,6 +41,7 @@ import org.eclipse.emf.cdo.util.StaleRevisionLockException; import org.eclipse.emf.cdo.view.CDOAdapterPolicy; import org.eclipse.emf.cdo.view.CDOFeatureAnalyzer; import org.eclipse.emf.cdo.view.CDOInvalidationPolicy; +import org.eclipse.emf.cdo.view.CDOLocksChangedEvent; import org.eclipse.emf.cdo.view.CDORevisionPrefetchingPolicy; import org.eclipse.emf.cdo.view.CDOStaleReferencePolicy; import org.eclipse.emf.cdo.view.CDOView; @@ -75,6 +81,7 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.spi.cdo.CDOSessionProtocol; import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; +import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; import org.eclipse.emf.spi.cdo.FSMUtil; import org.eclipse.emf.spi.cdo.InternalCDOObject; import org.eclipse.emf.spi.cdo.InternalCDOSession; @@ -257,18 +264,18 @@ public class CDOViewImpl extends AbstractCDOView checkActive(); checkState(getTimeStamp() == CDOBranchPoint.UNSPECIFIED_DATE, "Locking not supported for historial views"); - List<InternalCDORevision> revisions = new LinkedList<InternalCDORevision>(); + List<CDORevisionKey> revisionKeys = new LinkedList<CDORevisionKey>(); for (CDOObject object : objects) { InternalCDORevision revision = getRevision(object); if (revision != null) { - revisions.add(revision); + revisionKeys.add(revision); } } CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); - LockObjectsResult result = sessionProtocol.lockObjects(revisions, viewID, getBranch(), lockType, timeout); + LockObjectsResult result = sessionProtocol.lockObjects2(revisionKeys, viewID, getBranch(), lockType, timeout); if (!result.isSuccessful()) { @@ -286,6 +293,8 @@ public class CDOViewImpl extends AbstractCDOView throw new AssertionError("Unexpected lock result state"); } + updateLockStates(result.getNewLockStates()); + if (result.isWaitForUpdate()) { if (!getSession().options().isPassiveUpdateEnabled()) @@ -299,6 +308,29 @@ public class CDOViewImpl extends AbstractCDOView } } + protected void updateLockStates(CDOLockState[] newLockStates) + { + for (CDOLockState lockState : newLockStates) + { + Object o = lockState.getLockedObject(); + CDOID id; + if (o instanceof CDOID) + { + id = (CDOID)o; + } + else + { + id = ((CDOIDAndBranch)o).getID(); + } + + InternalCDOObject obj = getObject(id, false); + if (obj != null) + { + obj.cdoInternalSetLockState(lockState); + } + } + } + protected InternalCDORevision getRevision(CDOObject object) { if (object.cdoState() == CDOState.NEW) @@ -321,8 +353,21 @@ public class CDOViewImpl extends AbstractCDOView public synchronized void unlockObjects(Collection<? extends CDOObject> objects, LockType lockType) { checkActive(); + + List<CDOID> objectIDs = null; + if (objects != null) + { + objectIDs = new LinkedList<CDOID>(); + for (CDOObject obj : objects) + { + objectIDs.add(obj.cdoID()); + } + } + CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); - sessionProtocol.unlockObjects(this, objects, lockType); + UnlockObjectsResult result = sessionProtocol.unlockObjects2(this, objectIDs, lockType); + + updateLockStates(result.getNewLockStates()); } /** @@ -439,6 +484,13 @@ public class CDOViewImpl extends AbstractCDOView return revisionManager.getRevision(id, branchPoint, initialChunkSize, CDORevision.DEPTH_NONE, loadOnDemand); } + public CDOLockState[] getLockStates(Collection<CDOID> ids) + { + CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); + CDOLockState[] lockStates = sessionProtocol.getLockStates(viewID, ids); + return lockStates; + } + private CDOBranchPoint getBranchPointForID(CDOID id) { // If this view's timestamp is something other than UNSPECIFIED_DATE, @@ -876,6 +928,38 @@ public class CDOViewImpl extends AbstractCDOView } } + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo) + { + if (!options().isLockNotificationEnabled()) + { + return; + } + + // If lockChangeInfo pertains to a different view, do nothing. + if (!lockChangeInfo.getBranch().equals(getBranch())) + { + return; + } + + // If lockChangeInfo represents lock changes authored by this view itself, do nothing. + CDOLockOwner thisView = CDOLockUtil.createLockOwner(this); + if (lockChangeInfo.getLockOwner().equals(thisView)) + { + return; + } + + fireLocksChangedEvent(lockChangeInfo); + } + + private void fireLocksChangedEvent(CDOLockChangeInfo lockChangeInfo) + { + IListener[] listeners = getListeners(); + if (listeners != null) + { + fireEvent(new LocksChangedEvent(lockChangeInfo), listeners); + } + } + /** * @author Simon McDuff * @since 2.0 @@ -1304,6 +1388,47 @@ public class CDOViewImpl extends AbstractCDOView } /** + * @author Caspar De Groot + * @since 4.1 + */ + private final class LocksChangedEvent extends Event implements CDOLocksChangedEvent + { + private static final long serialVersionUID = 1L; + + private CDOLockChangeInfo lockChangeInfo; + + public LocksChangedEvent(CDOLockChangeInfo lockChangeInfo) + { + this.lockChangeInfo = lockChangeInfo; + } + + public CDOBranch getBranch() + { + return lockChangeInfo.getBranch(); + } + + public long getTimeStamp() + { + return lockChangeInfo.getTimeStamp(); + } + + public CDOLockOwner getLockOwner() + { + return lockChangeInfo.getLockOwner(); + } + + public CDOLockState[] getLockStates() + { + return lockChangeInfo.getLockStates(); + } + + public Operation getOperation() + { + return lockChangeInfo.getOperation(); + } + } + + /** * @author Eike Stepper * @since 2.0 */ @@ -1313,6 +1438,8 @@ public class CDOViewImpl extends AbstractCDOView private CDOInvalidationPolicy invalidationPolicy = CDOInvalidationPolicy.DEFAULT; + private boolean lockNotificationsEnabled; + private CDORevisionPrefetchingPolicy revisionPrefetchingPolicy = CDOUtil .createRevisionPrefetchingPolicy(NO_REVISION_PREFETCHING); @@ -1378,6 +1505,28 @@ public class CDOViewImpl extends AbstractCDOView fireEvent(event); } + public boolean isLockNotificationEnabled() + { + return lockNotificationsEnabled; + } + + public void setLockNotificationEnabled(boolean enabled) + { + IEvent event = null; + synchronized (CDOViewImpl.this) + { + if (enabled != lockNotificationsEnabled) + { + CDOSessionProtocol protocol = getSession().getSessionProtocol(); + protocol.enableLockNotifications(viewID, enabled); + lockNotificationsEnabled = enabled; + event = new LockNotificationEventImpl(); + } + } + + fireEvent(event); + } + public boolean hasChangeSubscriptionPolicies() { synchronized (CDOViewImpl.this) @@ -1662,6 +1811,19 @@ public class CDOViewImpl extends AbstractCDOView } /** + * @author Caspar De Groot + */ + private final class LockNotificationEventImpl extends OptionsEvent implements LockNotificationEvent + { + private static final long serialVersionUID = 1L; + + public LockNotificationEventImpl() + { + super(OptionsImpl.this); + } + } + + /** * @author Eike Stepper */ private final class RevisionPrefetchingPolicyEventImpl extends OptionsEvent implements diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java index 1032720deb..fdfc9111ed 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java @@ -24,6 +24,7 @@ import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.lob.CDOLob; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.protocol.CDOProtocol; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; @@ -45,6 +46,7 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager.RevisionLoader; import org.eclipse.emf.cdo.view.CDOView; +import org.eclipse.net4j.util.CheckUtil; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; import org.eclipse.net4j.util.om.monitor.OMMonitor; @@ -131,15 +133,41 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo public boolean cancelQuery(int queryId); /** + * Use #lockObjects2 instead. + * * @since 4.0 + * @deprecated Not called anymore. */ + @Deprecated public LockObjectsResult lockObjects(List<InternalCDORevision> viewedRevisions, int viewID, CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException; /** + * @since 4.1 + */ + public LockObjectsResult lockObjects2(List<CDORevisionKey> revisionKeys, int viewID, CDOBranch viewedBranch, + LockType lockType, long timeout) throws InterruptedException; + + /** + * @since 4.1 + */ + public LockObjectsResult delegateLockObjects(String lockAreaID, List<CDORevisionKey> revisionKeys, + CDOBranch viewedBranch, LockType lockType, long timeout) throws InterruptedException; + + /** * @since 3.0 */ - public void unlockObjects(CDOView view, Collection<? extends CDOObject> objects, LockType lockType); + public void unlockObjects(CDOView view, Collection<CDOID> objectIDs, LockType lockType); + + /** + * @since 4.1 + */ + public UnlockObjectsResult unlockObjects2(CDOView view, Collection<CDOID> objectIDs, LockType lockType); + + /** + * @since 4.1 + */ + public UnlockObjectsResult delegateUnlockObjects(String lockAreaID, Collection<CDOID> objectIDs, LockType lockType); /** * @since 3.0 @@ -233,6 +261,16 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo CDORevisionHandler handler); /** + * @since 4.1 + */ + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids); + + /** + * @since 4.1 + */ + public void enableLockNotifications(int viewID, boolean enable); + + /** * @author Eike Stepper * @since 3.0 * @noinstantiate This class is not intended to be instantiated by clients. @@ -606,6 +644,8 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo private CDOReferenceAdjuster referenceAdjuster; + private CDOLockState[] newLockStates; + /** * @since 4.0 */ @@ -697,6 +737,14 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo idMappings.put(oldID, newID); } + /** + * @since 4.1 + */ + public CDOLockState[] getNewLockStates() + { + return newLockStates; + } + protected PostCommitReferenceAdjuster createReferenceAdjuster() { return new PostCommitReferenceAdjuster(idProvider, new CDOIDMapper(idMappings)); @@ -735,6 +783,15 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo return idMapper.adjustReference(id, feature, index); } } + + /** + * @since 4.1 + */ + public void setNewLockStates(CDOLockState[] newLockStates) + { + CheckUtil.checkArg(newLockStates, "newLockStates"); + this.newLockStates = newLockStates; + } } /** @@ -752,14 +809,27 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo private CDORevisionKey[] staleRevisions; + private CDOLockState[] newLockStates; + + @Deprecated public LockObjectsResult(boolean successful, boolean timedOut, boolean waitForUpdate, long requiredTimestamp, CDORevisionKey[] staleRevisions) { + throw new AssertionError("Deprecated"); // TODO (CD) What to do about this?? + } + + /** + * @since 4.1 + */ + public LockObjectsResult(boolean successful, boolean timedOut, boolean waitForUpdate, long requiredTimestamp, + CDORevisionKey[] staleRevisions, CDOLockState[] newLockStates) + { this.successful = successful; this.timedOut = timedOut; this.waitForUpdate = waitForUpdate; this.requiredTimestamp = requiredTimestamp; this.staleRevisions = staleRevisions; + this.newLockStates = newLockStates; } public boolean isSuccessful() @@ -786,5 +856,31 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo { return staleRevisions; } + + /** + * @since 4.1 + */ + public CDOLockState[] getNewLockStates() + { + return newLockStates; + } + } + + /** + * @since 4.1 + */ + public static final class UnlockObjectsResult + { + private CDOLockState[] newLockStates; + + public UnlockObjectsResult(CDOLockState[] newLockStates) + { + this.newLockStates = newLockStates; + } + + public CDOLockState[] getNewLockStates() + { + return newLockStates; + } } } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java index ee269a96a8..4329baf19c 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOObject.java @@ -7,13 +7,14 @@ * * Contributors: * Eike Stepper - initial API and implementation - * Martin Flügge - enhancements + * Martin Fl�gge - enhancements */ package org.eclipse.emf.spi.cdo; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.model.CDOPackageTypeRegistry.CDOObjectMarker; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; @@ -55,6 +56,11 @@ public interface InternalCDOObject extends CDOObject, InternalEObject, InternalC public CDOState cdoInternalSetState(CDOState state); + /** + * @since 4.1 + */ + public void cdoInternalSetLockState(CDOLockState lockState); + public InternalEObject cdoInternalInstance(); public EStructuralFeature cdoInternalDynamicFeature(int dynamicFeatureID); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java index c82f67d8ad..2f0459575c 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOSession.java @@ -17,6 +17,7 @@ import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDGenerator; import org.eclipse.emf.cdo.common.lob.CDOLobStore; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; import org.eclipse.emf.cdo.common.protocol.CDOAuthenticator; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.session.CDORepositoryInfo; @@ -180,6 +181,11 @@ public interface InternalCDOSession extends CDOSession, PackageProcessor, Packag public void handleCommitNotification(CDOCommitInfo commitInfo); /** + * @since 4.1 + */ + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo); + + /** * @since 3.0 */ public void invalidate(CDOCommitInfo commitInfo, InternalCDOTransaction sender); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java index 345a1ae2ce..27ba5cc0b0 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/InternalCDOView.java @@ -15,6 +15,8 @@ import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDProvider; +import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; +import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; @@ -28,6 +30,7 @@ import org.eclipse.net4j.util.lifecycle.ILifecycle; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.ecore.EObject; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -133,6 +136,16 @@ public interface InternalCDOView extends CDOView, CDOIDProvider, ILifecycle public boolean hasSubscription(CDOID id); + /** + * @since 4.1 + */ + public void handleLockNotification(CDOLockChangeInfo lockChangeInfo); + + /** + * @since 4.1 + */ + public CDOLockState[] getLockStates(Collection<CDOID> ids); + // /** // * Each time CDORevision or CDOState of an CDOObject is modified, ensure that no concurrent access is modifying it // at diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java index 01f8d39f77..2b8a6218f0 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java @@ -43,6 +43,23 @@ public final class ObjectUtil return o.hashCode(); } + /** + * A collision-free hash code for small sets (<=4) of small, positive integers (<=128) + * + * @since 3.2 + */ + public static int hashCode(int... values) + { + int hash = 0; + for (int i = 0; i < values.length; i++) + { + hash += values[i]; + hash = (hash << 7) - hash; + } + + return hash; + } + public static int hashCode(long num) { return (int)(num >> 32) ^ (int)(num & 0xffffffff); diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/IRWOLockManager.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/IRWOLockManager.java new file mode 100644 index 0000000000..040ce5031f --- /dev/null +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/IRWOLockManager.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Caspar De Groot - initial API and implementation + */ +package org.eclipse.net4j.util.concurrent; + +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; + +import java.util.Collection; +import java.util.List; + +/** + * @author Caspar De Groot + * @since 3.2 + */ +public interface IRWOLockManager<OBJECT, CONTEXT> extends IRWLockManager<OBJECT, CONTEXT> +{ + public List<LockState<OBJECT, CONTEXT>> lock2(LockType type, CONTEXT context, + Collection<? extends OBJECT> objectsToLock, long timeout) throws InterruptedException; + + public List<LockState<OBJECT, CONTEXT>> unlock2(LockType type, CONTEXT context, + Collection<? extends OBJECT> objectsToUnlock); +} diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java index 979238a26a..846e9f3e35 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/RWOLockManager.java @@ -15,6 +15,7 @@ import org.eclipse.net4j.util.ObjectUtil; 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; @@ -33,20 +34,26 @@ import java.util.Set; * @author Caspar De Groot * @since 3.2 */ -public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLockManager<OBJECT, CONTEXT> +public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWOLockManager<OBJECT, CONTEXT> { - // TODO (CD) Ensure that CDOID and CDOIDandBranch have good hashCode implementations + private final List<LockState<OBJECT, CONTEXT>> EMPTY_RESULT = Collections.emptyList(); + private final Map<OBJECT, LockState<OBJECT, CONTEXT>> objectToLockStateMap = createObjectToLocksMap(); - // TODO (CD) Ensure that IView has a good hashCode implementation private final Map<CONTEXT, Set<LockState<OBJECT, CONTEXT>>> contextToLockStates = createContextToLocksMap(); public void lock(LockType type, CONTEXT context, Collection<? extends OBJECT> objectsToLock, long timeout) throws InterruptedException { + lock2(type, context, objectsToLock, timeout); + } + + public List<LockState<OBJECT, CONTEXT>> lock2(LockType type, CONTEXT context, + Collection<? extends OBJECT> objectsToLock, long timeout) throws InterruptedException + { if (objectsToLock.isEmpty()) { - return; + return EMPTY_RESULT; } // Must come before the synchronized block! @@ -56,7 +63,7 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc synchronized (this) { int count = objectsToLock.size(); - LockState<?, ?>[] lockStates = new LockState<?, ?>[count]; + List<LockState<OBJECT, CONTEXT>> lockStates = new ArrayList<LockState<OBJECT, CONTEXT>>(count); for (;;) { @@ -64,13 +71,12 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc { for (int i = 0; i < count; i++) { - @SuppressWarnings("unchecked") - LockState<OBJECT, CONTEXT> lockState = (LockState<OBJECT, CONTEXT>)lockStates[i]; + LockState<OBJECT, CONTEXT> lockState = lockStates.get(i); lockState.lock(type, context); addLockToContext(context, lockState); } - return; + return lockStates; } wait(startTime, timeout); @@ -86,9 +92,15 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc public synchronized void unlock(LockType type, CONTEXT context, Collection<? extends OBJECT> objectsToUnlock) { + unlock2(type, context, objectsToUnlock); + } + + public synchronized List<LockState<OBJECT, CONTEXT>> unlock2(LockType type, CONTEXT context, + Collection<? extends OBJECT> objectsToUnlock) + { if (objectsToUnlock.isEmpty()) { - return; + return EMPTY_RESULT; } List<LockState<OBJECT, CONTEXT>> lockStates = new LinkedList<LockState<OBJECT, CONTEXT>>(); @@ -121,14 +133,21 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc } notifyAll(); + + return lockStates; } public synchronized void unlock(CONTEXT context) { + unlock2(context); + } + + public synchronized List<LockState<OBJECT, CONTEXT>> unlock2(CONTEXT context) + { Set<LockState<OBJECT, CONTEXT>> lockStates = contextToLockStates.get(context); if (lockStates == null) { - return; + return EMPTY_RESULT; } List<OBJECT> objectsWithoutLocks = new LinkedList<OBJECT>(); @@ -141,8 +160,6 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc { lockState.unlock(lockType, context); } - - // TODO (CD) Consider whether WRITE_OPTIONs should be excluded from this... } if (lockState.hasNoLocks()) @@ -161,18 +178,35 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc } notifyAll(); + + return toList(lockStates); + } + + @SuppressWarnings("unchecked") + private List<LockState<OBJECT, CONTEXT>> toList(Set<LockState<OBJECT, CONTEXT>> lockStates) + { + if (lockStates instanceof List) + { + return (List<LockState<OBJECT, CONTEXT>>)lockStates; + } + + List<LockState<OBJECT, CONTEXT>> list = new LinkedList<LockState<OBJECT, CONTEXT>>(); + for (LockState<OBJECT, CONTEXT> lockState : lockStates) + { + list.add(lockState); + } + + return list; } public synchronized boolean hasLock(LockType type, CONTEXT context, OBJECT objectToLock) { - // TODO (CD) Should this be synced? LockState<OBJECT, CONTEXT> lockState = objectToLockStateMap.get(objectToLock); return lockState != null && lockState.hasLock(type, context, false); } public synchronized boolean hasLockByOthers(LockType type, CONTEXT context, OBJECT objectToLock) { - // TODO (CD) Should this be synced? LockState<OBJECT, CONTEXT> lockState = objectToLockStateMap.get(objectToLock); return lockState != null && lockState.hasLock(type, context, true); } @@ -222,6 +256,11 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc return contextToLockStates; } + public LockState<OBJECT, CONTEXT> getLockState(Object key) + { + return objectToLockStateMap.get(key); + } + private LockState<OBJECT, CONTEXT> getOrCreateLockState(OBJECT o) { LockState<OBJECT, CONTEXT> lockState = objectToLockStateMap.get(o); @@ -235,10 +274,10 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc } private boolean canLockInContext(LockType type, CONTEXT context, Collection<? extends OBJECT> objectsToLock, - LockState<?, ?>[] lockStatesToFill) + List<LockState<OBJECT, CONTEXT>> lockStatesToFill) { Iterator<? extends OBJECT> it = objectsToLock.iterator(); - for (int i = 0; i < lockStatesToFill.length; i++) + for (int i = 0; i < objectsToLock.size(); i++) { OBJECT o = it.next(); LockState<OBJECT, CONTEXT> lockState = getOrCreateLockState(o); @@ -247,7 +286,7 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc return false; } - lockStatesToFill[i] = lockState; + lockStatesToFill.add(lockState); } return true; @@ -301,7 +340,7 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc /** * Represents a combination of locks for one OBJECT. The different lock types are represented by the values of the - * enum {@link LockType}. + * enum {@link IRWLockManager.LockType} * <p> * The locking semantics established by this class are as follows: * <li>a read lock prevents a write lock by another, but allows read locks by others and allows a write option by @@ -315,11 +354,10 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc * @author Caspar De Groot * @since 3.2 */ - protected static class LockState<OBJECT, CONTEXT> + public static class LockState<OBJECT, CONTEXT> { private final OBJECT lockedObject; - // TODO (CD) Ensure that IView has a good hashCode implementation private final HashBag<CONTEXT> readLockOwners = new HashBag<CONTEXT>(); private CONTEXT writeLockOwner; @@ -374,6 +412,23 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc return false; } + public boolean hasLock(org.eclipse.net4j.util.concurrent.IRWLockManager.LockType type) + { + switch (type) + { + case READ: + return readLockOwners.size() > 0; + + case WRITE: + return writeLockOwner != null; + + case OPTION: + return writeOptionOwner != null; + } + + return false; + } + @Override public String toString() { @@ -648,5 +703,20 @@ public class RWOLockManager<OBJECT, CONTEXT> extends Lifecycle implements IRWLoc { writeOptionOwner = null; } + + public Set<CONTEXT> getReadLockOwners() + { + return Collections.unmodifiableSet(readLockOwners); + } + + public CONTEXT getWriteLockOwner() + { + return writeLockOwner; + } + + public CONTEXT getWriteOptionOwner() + { + return writeOptionOwner; + } } } |