Author: stefan
Date: Thu Mar 10 05:36:44 2005
New Revision: 156949
URL: http://svn.apache.org/viewcvs?view=rev&rev=156949
Log:
adding Locking support contributed by Dominique Pfister
Added:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java (with props)
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java (with props)
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java (with props)
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (with props)
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java (with props)
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java (with props)
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java (with props)
Modified:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Constants.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/SerializationTest.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/TestAll.java
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Constants.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Constants.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Constants.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Constants.java Thu Mar 10 05:36:44 2005
@@ -84,6 +84,12 @@
/** jcr:mergeFailed */
public static final QName JCR_MERGEFAILED = new QName(NS_JCR_URI, "mergeFailed");
+ /** jcr:lockOwner */
+ public static final QName JCR_LOCKOWNER = new QName(NS_JCR_URI, "lockOwner");
+
+ /** jcr:lockIsDeep */
+ public static final QName JCR_LOCKISDEEP = new QName(NS_JCR_URI, "lockIsDeep");
+
/**
* root path for version storage
*/
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java Thu Mar 10 05:36:44 2005
@@ -1094,6 +1094,11 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ if (!noChecks) {
+ parentNode.checkLock();
+ }
+
// delegate the removal of the child item to the parent node
if (isNode()) {
parentNode.removeChildNode(thisName.getName(), thisName.getIndex());
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java Thu Mar 10 05:36:44 2005
@@ -16,6 +16,7 @@
*/
package org.apache.jackrabbit.core;
+import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.core.nodetype.ChildNodeDef;
import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
import org.apache.jackrabbit.core.nodetype.NodeDefId;
@@ -665,6 +666,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ parentNode.checkLock();
+
// delegate the creation of the child node to the parent node
return parentNode.internalAddChildNode(nodeName, nodeType, uuid);
}
@@ -914,6 +918,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
// check protected flag
if (definition.isProtected()) {
String msg = safeGetJCRPath() + ": cannot add a mixin node type to a protected node";
@@ -921,6 +928,9 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ checkLock();
+
NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
if (!mixin.isMixin()) {
@@ -1024,6 +1034,9 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ checkLock();
+
// check if mixin is assigned
if (!((NodeState) state).getMixinTypeNames().contains(mixinName)) {
throw new NoSuchNodeTypeException();
@@ -1420,6 +1433,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
NodeTypeImpl nt = (nodeTypeName == null) ?
null : session.getNodeTypeManager().getNodeType(nodeTypeName);
return internalAddChildNode(nodeName, nt, uuid);
@@ -1441,6 +1457,7 @@
public PropertyImpl setProperty(QName name, Value[] values)
throws ValueFormatException, VersionException, LockException,
RepositoryException {
+
int type;
if (values == null || values.length == 0
|| values[0] == null) {
@@ -1478,6 +1495,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, type, true, status);
try {
@@ -1519,6 +1539,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
int type = (value == null) ? PropertyType.UNDEFINED : value.getType();
BitSet status = new BitSet();
@@ -1700,6 +1723,9 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ checkLock();
+
ArrayList list = new ArrayList(((NodeState) state).getChildNodeEntries());
int srcInd = -1, destInd = -1;
for (int i = 0; i < list.size(); i++) {
@@ -1789,6 +1815,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, type, true, status);
try {
@@ -1833,6 +1862,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, type, true, status);
try {
@@ -1864,6 +1896,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
/**
* if the target property is not of type STRING then a
* best-effort conversion is tried
@@ -1899,6 +1934,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
int type = (value == null) ? PropertyType.UNDEFINED : value.getType();
BitSet status = new BitSet();
@@ -1932,6 +1970,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.BINARY, false, status);
try {
@@ -1963,6 +2004,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.BOOLEAN, false, status);
try {
@@ -1994,6 +2038,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.DOUBLE, false, status);
try {
@@ -2025,6 +2072,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.LONG, false, status);
try {
@@ -2056,6 +2106,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.DATE, false, status);
try {
@@ -2087,6 +2140,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
BitSet status = new BitSet();
PropertyImpl prop = getOrCreateProperty(name, PropertyType.REFERENCE, false, status);
try {
@@ -2616,6 +2672,9 @@
throw new VersionException(msg);
}
+ // check lock status
+ checkLock();
+
Version v = session.getVersionManager().checkin(this);
Property prop = internalSetProperty(JCR_ISCHECKEDOUT, InternalValue.create(false));
prop.save();
@@ -2645,6 +2704,9 @@
return;
}
+ // check lock status
+ checkLock();
+
Property prop = internalSetProperty(JCR_ISCHECKEDOUT, InternalValue.create(true));
prop.save();
prop = internalSetProperty(JCR_PREDECESSORS,
@@ -2670,6 +2732,9 @@
throw new InvalidItemStateException(msg);
}
+ // check lock status
+ checkLock();
+
// @todo FIXME need to get session with same credentials as current
SessionImpl srcSession = rep.getSystemSession(srcWorkspaceName);
@@ -2722,6 +2787,9 @@
return;
}
+ // check lock status
+ checkLock();
+
// @todo FIXME need to get session with same credentials as current
SessionImpl srcSession = rep.getSystemSession(srcWorkspace);
@@ -2846,6 +2914,9 @@
throw new InvalidItemStateException(msg);
}
+ // check lock status
+ checkLock();
+
GenericVersionSelector gvs = new GenericVersionSelector();
gvs.setName(versionName);
internalRestore(getVersionHistory().getVersion(versionName), gvs, removeExisting);
@@ -2869,6 +2940,9 @@
throw new InvalidItemStateException(msg);
}
+ // check lock status
+ checkLock();
+
// check if 'own' version
// TODO: change if Version.getContainingVersionHistory() is introduced
if (!version.getParent().isSame(getVersionHistory())) {
@@ -2896,6 +2970,9 @@
throw new InvalidItemStateException(msg);
}
+ // check lock status
+ checkLock();
+
// if node exists, do a 'normal' restore
if (hasNode(relPath)) {
getNode(relPath).restore(version, removeExisting);
@@ -2924,6 +3001,9 @@
throw new InvalidItemStateException(msg);
}
+ // check lock status
+ checkLock();
+
Version v = getVersionHistory().getVersionByLabel(versionLabel);
if (v == null) {
throw new VersionException("No version for label " + versionLabel + " found.");
@@ -3232,7 +3312,11 @@
* @throws RepositoryException
*/
private void internalUpdate(NodeImpl srcNode, boolean removeExisting, boolean replaceExisting)
- throws RepositoryException {
+ throws LockException, RepositoryException {
+
+ // check lock status
+ checkLock();
+
/*
* The "state" of the node in this context means the set of properties and
* child nodes it has. In other words, when a node is updated, its set of
@@ -3294,6 +3378,9 @@
throw new ConstraintViolationException(msg, re);
}
+ // check lock status of current parent
+ ((NodeImpl) dstNode.getParent()).checkLock();
+
// add target to new parent and remove from old one
createChildNodeLink(child.getQName(), uuid);
((NodeImpl) dstNode.getParent()).removeChildNode(child.getQName(), child.getIndex() == 0 ? 1 : child.getIndex());
@@ -3550,15 +3637,30 @@
// check for pending changes
if (hasPendingChanges()) {
- String msg = "Unable to checkin node. Node has pending changes: " + safeGetJCRPath();
+ String msg = "Unable to lock node. Node has pending changes: " + safeGetJCRPath();
log.debug(msg);
throw new InvalidItemStateException(msg);
}
checkLockable();
- // @todo implement locking support
- throw new UnsupportedRepositoryOperationException();
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ Lock lock = lockMgr.lock(this, isDeep, isSessionScoped);
+
+ try {
+ internalSetProperty(JCR_LOCKOWNER,
+ InternalValue.create(session.getUserId()));
+ internalSetProperty(JCR_LOCKISDEEP,
+ InternalValue.create(isDeep));
+ save();
+
+ } catch (RepositoryException e) {
+
+ // An error occurred, so remove lock
+ lockMgr.unlock(this);
+ throw e;
+ }
+ return lock;
}
/**
@@ -3572,8 +3674,8 @@
checkLockable();
- // @todo implement locking support
- throw new UnsupportedRepositoryOperationException();
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ return lockMgr.getLock(this);
}
/**
@@ -3588,15 +3690,19 @@
// check for pending changes
if (hasPendingChanges()) {
- String msg = "Unable to checkin node. Node has pending changes: " + safeGetJCRPath();
+ String msg = "Unable to unlock node. Node has pending changes: " + safeGetJCRPath();
log.debug(msg);
throw new InvalidItemStateException(msg);
}
checkLockable();
- // @todo implement locking support
- throw new UnsupportedRepositoryOperationException();
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ lockMgr.unlock(this);
+
+ removeChildProperty(JCR_LOCKOWNER);
+ removeChildProperty(JCR_LOCKISDEEP);
+ save();
}
/**
@@ -3606,8 +3712,10 @@
// check state of this instance
sanityCheck();
- // @todo implement locking support
- return false;
+ checkLockable();
+
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ return lockMgr.holdsLock(this);
}
/**
@@ -3617,8 +3725,8 @@
// check state of this instance
sanityCheck();
- // @todo implement locking support
- return false;
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ return lockMgr.isLocked(this);
}
/**
@@ -3635,5 +3743,16 @@
log.debug(msg);
throw new UnsupportedRepositoryOperationException(msg);
}
+ }
+
+ /**
+ * Check whether this node is locked by somebody else.
+ *
+ * @throws LockException if this node is locked by somebody else
+ * @throws RepositoryException if some other error occurs
+ */
+ protected void checkLock() throws LockException, RepositoryException {
+ LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
+ lockMgr.checkLock(this);
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java Thu Mar 10 05:36:44 2005
@@ -539,6 +539,9 @@
throw new VersionException("cannot set the value of a property of a checked-in node " + safeGetJCRPath());
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check protected flag
if (definition.isProtected()) {
throw new ConstraintViolationException("cannot set the value of a protected property " + safeGetJCRPath());
@@ -596,6 +599,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -638,6 +644,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -691,6 +700,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -737,6 +749,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is not multi-valued");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -790,6 +805,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -832,6 +850,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -886,6 +907,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -930,6 +954,9 @@
throw new ValueFormatException(safeGetJCRPath() + " is multi-valued and can therefore only be set to an array of values");
}
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
+
// check type according to definition of this property
int reqType = definition.getRequiredType();
if (reqType == PropertyType.UNDEFINED) {
@@ -975,6 +1002,9 @@
if (!definition.isMultiple()) {
throw new ValueFormatException(safeGetJCRPath() + " is not multi-valued");
}
+
+ // check lock status
+ ((NodeImpl) getParent()).checkLock();
int reqType = definition.getRequiredType();
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java Thu Mar 10 05:36:44 2005
@@ -39,6 +39,8 @@
import org.apache.jackrabbit.core.version.VersionManager;
import org.apache.jackrabbit.core.version.VersionManagerImpl;
import org.apache.jackrabbit.core.version.persistence.NativePVM;
+import org.apache.jackrabbit.core.lock.LockManager;
+import org.apache.jackrabbit.core.lock.LockManagerImpl;
import org.apache.log4j.Logger;
import javax.jcr.Credentials;
@@ -86,6 +88,11 @@
private static final String PROPERTIES_RESOURCE = "rep.properties";
private final Properties repProps;
+ /**
+ * Name of the lock file, relative to the workspace home directory
+ */
+ private static final String LOCKS_FILE = "locks";
+
// names of well-known repository properties
public static final String STATS_NODE_COUNT_PROPERTY = "jcr.repository.stats.nodes.count";
public static final String STATS_PROP_COUNT_PROPERTY = "jcr.repository.stats.properties.count";
@@ -469,6 +476,28 @@
return wspInfo.getSearchManager();
}
+ /**
+ * Returns the {@link LockManager} for the workspace with name
+ * <code>workspaceName</code>
+ * @param workspaceName workspace name
+ * @return <code>LockManager</code> for the workspace
+ * @throws NoSuchWorkspaceException if such a workspace does not exist
+ * @throws RepositoryException if some other error occurs
+ */
+ LockManager getLockManager(String workspaceName) throws
+ NoSuchWorkspaceException, RepositoryException {
+
+ if (disposed) {
+ throw new IllegalStateException("repository instance has been shut down");
+ }
+
+ WorkspaceInfo wspInfo = (WorkspaceInfo) wspInfos.get(workspaceName);
+ if (wspInfo == null) {
+ throw new NoSuchWorkspaceException(workspaceName);
+ }
+ return wspInfo.getLockManager();
+ }
+
SystemSession getSystemSession(String workspaceName)
throws NoSuchWorkspaceException, RepositoryException {
// check state
@@ -830,6 +859,11 @@
private SearchManager searchMgr;
/**
+ * Lock manager
+ */
+ private LockManagerImpl lockMgr;
+
+ /**
* Creates a new <code>WorkspaceInfo</code> based on the given
* <code>config</code>.
*
@@ -957,6 +991,20 @@
}
/**
+ * Returns the lock manager for this workspace
+ *
+ * @return the lock manager for this workspace
+ * @throws RepositoryException if the lock manager could not be created
+ */
+ synchronized LockManager getLockManager() throws RepositoryException {
+ if (lockMgr == null) {
+ lockMgr = new LockManagerImpl(getSystemSession(),
+ new File(config.getHomeDir(), LOCKS_FILE));
+ }
+ return lockMgr;
+ }
+
+ /**
* Disposes all objects this <code>WorkspaceInfo</code> is holding.
*/
void dispose() {
@@ -993,6 +1041,10 @@
log.error("error while closing persistence manager of workspace " + config.getName(), e);
}
persistMgr = null;
+ }
+
+ if (lockMgr != null) {
+ lockMgr.close();
}
// close workspace file system
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java Thu Mar 10 05:36:44 2005
@@ -73,9 +73,11 @@
import java.io.PrintStream;
import java.security.AccessControlException;
import java.security.Principal;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -166,6 +168,11 @@
protected final Map listeners = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
/**
+ * Lock tokens
+ */
+ protected final List lockTokens = new ArrayList();
+
+ /**
* Protected constructor.
*
* @param rep
@@ -310,7 +317,7 @@
*
* @return the <code>ItemManager</code>
*/
- protected ItemManager getItemManager() {
+ public ItemManager getItemManager() {
return itemMgr;
}
@@ -845,6 +852,9 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ destParentNode.checkLock();
+
// do move operation
String targetUUID = ((NodeState) targetNode.getItemState()).getUUID();
@@ -899,6 +909,9 @@
throw new ConstraintViolationException(msg);
}
+ // check lock status
+ parent.checkLock();
+
SessionImporter importer = new SessionImporter(parent, this, Importer.IMPORT_UUID_CREATE_NEW);
return new ImportHandler(importer, getNamespaceResolver(), rep.getNamespaceRegistry());
}
@@ -1108,23 +1121,62 @@
* {@inheritDoc}
*/
public void addLockToken(String lt) {
- // @todo implement locking support
- throw new UnsupportedOperationException("Locking not implemented yet.");
+ addLockToken(lt, true);
+ }
+
+ /**
+ * Internal implementation of {@link #addLockToken(String)}. Additionally
+ * takes a parameter indicating whether the lock manager needs to be
+ * informed.
+ */
+ public void addLockToken(String lt, boolean notify) {
+ synchronized (lockTokens) {
+ lockTokens.add(lt);
+
+ if (notify) {
+ try {
+ wsp.getLockManager().lockTokenAdded(this, lt);
+ } catch (RepositoryException e) {
+ log.error("Lock manager not available.", e);
+ }
+ }
+ }
}
/**
* {@inheritDoc}
*/
public String[] getLockTokens() {
- // @todo implement locking support
- return new String[0];
+ synchronized (lockTokens) {
+ String[] result = new String[lockTokens.size()];
+ lockTokens.toArray(result);
+ return result;
+ }
}
/**
* {@inheritDoc}
*/
public void removeLockToken(String lt) {
- // @todo implement locking support
- throw new UnsupportedOperationException("Locking not implemented yet.");
+ removeLockToken(lt, true);
+ }
+
+ /**
+ * Internal implementation of {@link #removeLockToken(String)}. Additionally
+ * takes a parameter indicating whether the lock manager needs to be
+ * informed.
+ */
+ public void removeLockToken(String lt, boolean notify) {
+ synchronized (lockTokens) {
+ lockTokens.remove(lt);
+
+ if (notify) {
+ try {
+ wsp.getLockManager().lockTokenRemoved(this, lt);
+ } catch (RepositoryException e) {
+ log.error("Lock manager not available.", e);
+ }
+ }
+ }
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java Thu Mar 10 05:36:44 2005
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.core;
import org.apache.jackrabbit.core.config.WorkspaceConfig;
+import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.core.nodetype.ChildNodeDef;
import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
import org.apache.jackrabbit.core.nodetype.NodeDefId;
@@ -114,6 +115,11 @@
protected final SessionImpl session;
/**
+ * The <code>LockManager</code> for this <code>Workspace</code>
+ */
+ protected LockManager lockMgr;
+
+ /**
* Package private constructor.
*
* @param wspConfig
@@ -279,7 +285,7 @@
*/
protected void checkRemoveNode(Path nodePath)
throws ConstraintViolationException, AccessDeniedException,
- PathNotFoundException, RepositoryException {
+ PathNotFoundException, LockException, RepositoryException {
AccessManager accessMgr = session.getAccessManager();
@@ -577,7 +583,8 @@
// make sure destination parent node is checked-out
verifyCheckedOut(destParentPath);
- // @todo check locked-status
+ // check lock status
+ getLockManager().checkLock(destParentPath, session);
// 2. check access rights & node type constraints
@@ -625,6 +632,24 @@
}
}
+ /**
+ * Return the lock manager for this workspace. If not already done, creates
+ * a new instance.
+ *
+ * @return lock manager for this workspace
+ * @throws RepositoryException if an error occurs
+ */
+ public synchronized LockManager getLockManager() throws RepositoryException {
+
+ // check state of this instance
+ sanityCheck();
+
+ if (lockMgr == null) {
+ lockMgr = rep.getLockManager(wspConfig.getName());
+ }
+ return lockMgr;
+ }
+
//------------------------------------------------------------< Workspace >
/**
* {@inheritDoc}
@@ -804,7 +829,8 @@
verifyCheckedOut(srcParentPath);
verifyCheckedOut(destParentPath);
- // @todo check locked-status
+ // check locked-status
+ getLockManager().checkLock(destParentPath, session);
// 2. check node type constraints & access rights
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+/**
+ * Implementation of a <code>Lock</code> that gets returned to clients asking
+ * for a lock.
+ */
+class LockImpl implements Lock {
+
+ /**
+ * Lock info containing latest information
+ */
+ private final LockInfo info;
+
+ /**
+ * Node holding lock
+ */
+ private Node node;
+
+ /**
+ * Create a new instance of this class.
+ * @param info lock information
+ * @param node node holding lock
+ */
+ public LockImpl(LockInfo info, Node node) {
+ this.info = info;
+ this.node = node;
+ }
+
+ //------------------------------------------------------------------< Lock >
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLockOwner() {
+ return info.lockOwner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDeep() {
+ return info.deep;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLockToken() {
+ try {
+ return info.getLockToken(node.getSession());
+ } catch (RepositoryException e) {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLive() {
+ return info.isLive();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refresh() throws LockException, RepositoryException {
+ //@todo implement refresh
+ }
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import org.apache.jackrabbit.core.SessionListener;
+import org.apache.jackrabbit.core.SessionImpl;
+
+import javax.jcr.Session;
+
+/**
+ * Contains information about a lock and gets placed inside the child
+ * information of a {@link PathMap}.
+ */
+class LockInfo implements SessionListener {
+
+ /**
+ * Lock token
+ */
+ final LockToken lockToken;
+
+ /**
+ * Flag indicating whether lock is session scoped
+ */
+ final boolean sessionScoped;
+
+ /**
+ * Flag indicating whether lock is deep
+ */
+ final boolean deep;
+
+ /**
+ * Lock owner, determined on creation time
+ */
+ final String lockOwner;
+
+ /**
+ * Session currently holding lock
+ */
+ private SessionImpl lockHolder;
+
+ /**
+ * Flag indicating whether this lock is live
+ */
+ private boolean live;
+
+ /**
+ * Create a new instance of this class.
+ * @param lockToken lock token
+ * @param sessionScoped whether lock token is session scoped
+ * @param deep whether lock is deep
+ * @param lockOwner owner of lock
+ */
+ public LockInfo(LockToken lockToken, boolean sessionScoped,
+ boolean deep, String lockOwner) {
+ this.lockToken = lockToken;
+ this.sessionScoped = sessionScoped;
+ this.deep = deep;
+ this.lockOwner = lockOwner;
+ this.live = true;
+ }
+
+ /**
+ * Set the live flag
+ * @param live live flag
+ */
+ public void setLive(boolean live) {
+ this.live = live;
+ }
+
+ /**
+ * Return the UUID of the lock holding node
+ * @return uuid
+ */
+ public String getUUID() {
+ return lockToken.uuid;
+ }
+
+ /**
+ * Return the session currently holding the lock
+ * @return session currently holding the lock
+ */
+ public SessionImpl getLockHolder() {
+ return lockHolder;
+ }
+
+ /**
+ * Set the session currently holding the lock
+ * @param lockHolder session currently holding the lock
+ */
+ public void setLockHolder(SessionImpl lockHolder) {
+ this.lockHolder = lockHolder;
+ }
+
+ /**
+ * Return the lock token as seen by the session passed as parameter. If
+ * this session is currently holding the lock, it will get the lock token
+ * itself, otherwise a <code>null</code> string
+ */
+ public String getLockToken(Session session) {
+ if (live && session.equals(lockHolder)) {
+ return lockToken.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Return a flag indicating whether the lock is live
+ * @return <code>true</code> if the lock is live; otherwise <code>false</code>
+ */
+ public boolean isLive() {
+ return live;
+ }
+
+ //-------------------------------------------------------< SessionListener >
+
+ /**
+ * {@inheritDoc}
+ */
+ public void loggedOut(SessionImpl session) {
+ live = false;
+ }
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+
+import javax.jcr.lock.LockException;
+import javax.jcr.lock.Lock;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+/**
+ * Defines the functionality needed for locking and unlocking nodes.
+ */
+public interface LockManager {
+
+ /**
+ * Lock a node. Checks whether the node is not locked and then
+ * returns a lock object for this node.
+ * @param node node
+ * @param isDeep whether the lock applies to this node only
+ * @param isSessionScoped whether the lock is session scoped
+ * @return lock object
+ * @throws LockException if this node already is locked, or some descendant
+ * node is locked and <code>isDeep</code> is <code>true</code>
+ * @see javax.jcr.Node#lock
+ */
+ public Lock lock(NodeImpl node, boolean isDeep, boolean isSessionScoped)
+ throws LockException, RepositoryException;
+
+ /**
+ * Returns the Lock object that applies to a node. This may be either a lock
+ * on this node itself or a deep lock on a node above this node.
+ * @param node node
+ * @return lock object
+ * @throws LockException if this node is not locked
+ * @see javax.jcr.Node#getLock
+ */
+ public Lock getLock(NodeImpl node) throws LockException, RepositoryException;
+
+ /**
+ * Removes the lock on a node given by its path.
+ * @param node node
+ * @throws LockException if this node is not locked or the session
+ * does not have the correct lock token
+ * @see javax.jcr.Node#unlock
+ */
+ public void unlock(NodeImpl node) throws LockException, RepositoryException;
+
+ /**
+ * Returns <code>true</code> if the node given holds a lock;
+ * otherwise returns <code>false</code>
+ * @param node node
+ * @return <code>true</code> if the node given holds a lock;
+ * otherwise returns <code>false</code>
+ * @see javax.jcr.Node#holdsLock
+ */
+ public boolean holdsLock(NodeImpl node) throws RepositoryException;
+
+ /**
+ * Returns <code>true</code> if this node is locked either as a result
+ * of a lock held by this node or by a deep lock on a node above this
+ * node; otherwise returns <code>false</code>
+ * @param node node
+ * @return <code>true</code> if this node is locked either as a result
+ * of a lock held by this node or by a deep lock on a node above this
+ * node; otherwise returns <code>false</code>
+ * @see javax.jcr.Node#isLocked
+ */
+ public boolean isLocked(NodeImpl node) throws RepositoryException;
+
+ /**
+ * Check whether the node given is locked by somebody else than the
+ * current session. Access is allowed if the node is not locked or
+ * if the session itself holds the lock to this node, i.e. the session
+ * contains the lock token for the lock.
+ * @param node node to check
+ * @throws LockException if write access to the specified node is not allowed
+ * @throws RepositoryException if some other error occurs
+ */
+ public void checkLock(NodeImpl node)
+ throws LockException, RepositoryException;
+
+ /**
+ * Check whether the path given is locked by somebody else than the
+ * session described. Access is allowed if the node is not locked or
+ * if the session itself holds the lock to this node, i.e. the session
+ * contains the lock token for the lock.
+ * @param path path to check
+ * @param session session
+ * @throws LockException if write access to the specified path is not allowed
+ * @throws RepositoryException if some other error occurs
+ */
+ public void checkLock(Path path, Session session)
+ throws LockException, RepositoryException;
+
+ /**
+ * Invoked by a session to inform that a lock token has been added.
+ * @param session session that has a added lock token
+ * @param lt added lock token
+ */
+ public void lockTokenAdded(SessionImpl session, String lt);
+
+ /**
+ * Invoked by a session to inform that a lock token has been removed.
+ * @param session session that has a removed lock token
+ * @param lt removed lock token
+ */
+ public void lockTokenRemoved(SessionImpl session, String lt);
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.jackrabbit.core.observation.EventImpl;
+import org.apache.log4j.Logger;
+
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.io.*;
+
+/**
+ * Provides the functionality needed for locking and unlocking nodes.
+ */
+public class LockManagerImpl implements LockManager, SynchronousEventListener {
+
+ /**
+ * Logger
+ */
+ private static final Logger log = Logger.getLogger(LockManagerImpl.class);
+
+ /**
+ * Path map containing all locks at the leaves
+ */
+ private final PathMap lockMap = new PathMap();
+
+ /**
+ * System session
+ */
+ private final SessionImpl session;
+
+ /**
+ * Locks file
+ */
+ private final File locksFile;
+
+ /**
+ * Name space resolver
+ */
+ private final NamespaceResolver nsResolver;
+
+ /**`
+ * Map of nodes that been removed and may be re-added as result
+ * of a move operation
+ */
+ private final Map zombieNodes = new HashMap();
+
+ /**
+ * Create a new instance of this class.
+ * @param session system session
+ * @param locksFile file locks file to use
+ * @throws RepositoryException if an error occurs
+ */
+ public LockManagerImpl(SessionImpl session, File locksFile)
+ throws RepositoryException {
+
+ this.session = session;
+ this.nsResolver = session.getNamespaceResolver();
+ this.locksFile = locksFile;
+
+ ((WorkspaceImpl) session.getWorkspace()).getObservationManager().
+ addEventListener(this, Event.NODE_ADDED|Event.NODE_REMOVED,
+ "/", true, null, null, true);
+
+ if (locksFile.exists()) {
+ try {
+ load();
+ } catch (IOException e) {
+ throw new RepositoryException(
+ "I/O error while reading locks from '" +
+ locksFile.getPath() + "'", e);
+ }
+ }
+ }
+
+ /**
+ * Close this lock manager. Writes back all changes.
+ */
+ public void close() {
+ try {
+ save();
+ } catch (IOException e) {
+ log.warn("I/O error while saving locks to '" +
+ locksFile.getPath() + "': " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * Read locks from locks file and populate path map
+ */
+ private void load() throws IOException {
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(locksFile));
+ while (true) {
+ String s = reader.readLine();
+ if (s == null || s.equals("")) {
+ break;
+ }
+ reapplyLock(LockToken.parse(s));
+ }
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e2) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ /**
+ * Reapply a lock given a lock token that was read from the locks file
+ * @param lockToken lock token to apply
+ */
+ private void reapplyLock(LockToken lockToken) {
+ try {
+ NodeImpl node = (NodeImpl) session.getNodeByUUID(lockToken.uuid);
+ Path path = node.getPrimaryPath();
+
+ LockInfo info = new LockInfo(lockToken, false,
+ node.getProperty(Constants.JCR_LOCKISDEEP).getBoolean(),
+ node.getProperty(Constants.JCR_LOCKOWNER).getString());
+ lockMap.put(path, info);
+ } catch (RepositoryException e) {
+ log.warn("Unable to recreate lock '" + lockToken +
+ "': " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * Write locks to locks file
+ */
+ private void save() throws IOException {
+ final ArrayList list = new ArrayList();
+
+ lockMap.traverse(new PathMap.ChildVisitor() {
+ public void childVisited(PathMap.Child child) {
+ LockInfo info = (LockInfo) child.get();
+ if (!info.sessionScoped) {
+ list.add(info);
+ }
+ }
+ }, false);
+
+
+ BufferedWriter writer = null;
+
+ try {
+ writer = new BufferedWriter(new FileWriter(locksFile));
+ for (int i = 0; i < list.size(); i++) {
+ LockInfo info = (LockInfo) list.get(i);
+ writer.write(info.lockToken.toString());
+ writer.newLine();
+ }
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ //-----------------------------------------------------------< LockManager >
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized Lock lock(NodeImpl node, boolean isDeep,
+ boolean isSessionScoped)
+ throws LockException, RepositoryException {
+
+ Path path = node.getPrimaryPath();
+ PathMap.Child child = lockMap.map(path, false);
+
+ LockInfo info = (LockInfo) child.get();
+ if (info != null) {
+ if (child.hasPath(path)) {
+ throw new LockException("Node already locked: " + path);
+ } else if (info.deep) {
+ throw new LockException("Parent node has deep lock.");
+ }
+ }
+ if (isDeep && child.hasPath(path)) {
+ throw new LockException("Some child node is locked.");
+ }
+
+ SessionImpl session = (SessionImpl) node.getSession();
+ info = new LockInfo(new LockToken(node.internalGetUUID()),
+ isSessionScoped, isDeep, session.getUserId());
+ info.setLockHolder(session);
+ if (isSessionScoped) {
+ session.addListener(info);
+ }
+ session.addLockToken(info.lockToken.toString(), false);
+ lockMap.put(path, info);
+ return new LockImpl(info, node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized Lock getLock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ Path path = node.getPrimaryPath();
+
+ PathMap.Child child = lockMap.map(path, false);
+ LockInfo info = (LockInfo) child.get();
+ if (info == null) {
+ throw new LockException("Node not locked: " + path);
+ }
+ if (child.hasPath(path) || info.deep) {
+ return new LockImpl(info, node);
+ } else {
+ throw new LockException("Node not locked: " + path);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized void unlock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ Path path = node.getPrimaryPath();
+
+ PathMap.Child child = lockMap.map(path, true);
+ if (child == null) {
+ throw new LockException("Node not locked: " + path);
+ }
+
+ LockInfo info = (LockInfo) child.get();
+ if (info == null) {
+ throw new LockException("Node not locked: " + path);
+ }
+ if (!node.getSession().equals(info.getLockHolder())) {
+ throw new LockException("Node not locked by session: " + path);
+ }
+ child.remove();
+
+ info.setLive(false);
+ info.setLockHolder(null);
+ session.removeLockToken(info.lockToken.toString(), false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized boolean holdsLock(NodeImpl node) throws RepositoryException {
+ PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
+ if (child == null) {
+ return false;
+ }
+ return child.get() != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized boolean isLocked(NodeImpl node) throws RepositoryException {
+ Path path = node.getPrimaryPath();
+
+ PathMap.Child child = lockMap.map(path, false);
+ LockInfo info = (LockInfo) child.get();
+ if (info == null) {
+ return false;
+ }
+ if (child.hasPath(path)) {
+ return true;
+ } else {
+ return info.deep;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ checkLock(node.getPrimaryPath(), node.getSession());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(Path path, Session session)
+ throws LockException, RepositoryException {
+
+ PathMap.Child child = lockMap.map(path, false);
+ LockInfo info = (LockInfo) child.get();
+ if (info != null) {
+ if (child.hasPath(path) || info.deep) {
+ if (!session.equals(info.getLockHolder())) {
+ throw new LockException("Node locked.");
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenAdded(SessionImpl session, String lt) {
+ try {
+ LockToken lockToken = LockToken.parse(lt);
+
+ NodeImpl node = (NodeImpl) session.getItemManager().
+ getItem(new NodeId(lockToken.uuid));
+ PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
+ if (child != null) {
+ LockInfo info = (LockInfo) child.get();
+ if (info != null) {
+ if (info.getLockHolder() == null) {
+ info.setLockHolder(session);
+ } else {
+ log.warn("Adding lock token has no effect: " +
+ "lock already held by other session.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad lock token: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.warn("Unable to set lock holder: " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenRemoved(SessionImpl session, String lt) {
+ try {
+ LockToken lockToken = LockToken.parse(lt);
+
+ NodeImpl node = (NodeImpl) session.getItemManager().
+ getItem(new NodeId(lockToken.uuid));
+ PathMap.Child child = lockMap.map(node.getPrimaryPath(), true);
+ if (child != null) {
+ LockInfo info = (LockInfo) child.get();
+ if (info != null) {
+ if (session.equals(info.getLockHolder())) {
+ info.setLockHolder(null);
+ } else {
+ log.warn("Removing lock token has no effect: " +
+ "lock held by other session.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad lock token: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.warn("Unable to reset lock holder: " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ //----------------------------------------------< SynchronousEventListener >
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onEvent(EventIterator events) {
+ while (events.hasNext()) {
+ EventImpl event = (EventImpl) events.nextEvent();
+ switch (event.getType()) {
+ case Event.NODE_ADDED:
+ try {
+ childAdded(event.getChildUUID(),
+ Path.create(event.getPath(), nsResolver, true));
+ } catch (MalformedPathException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ }
+ break;
+ case Event.NODE_REMOVED:
+ try {
+ childRemoved(event.getChildUUID(),
+ Path.create(event.getPath(), nsResolver, true));
+ } catch (MalformedPathException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Invoked when some child has been added.
+ */
+ private synchronized void childAdded(String uuid, Path path) {
+ try {
+ PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
+ if (parent != null) {
+ parent.insertChild(path.getNameElement());
+ }
+ PathMap.Child zombie = (PathMap.Child) zombieNodes.remove(uuid);
+ if (zombie != null) {
+ lockMap.resurrect(path, zombie);
+ }
+ } catch (PathNotFoundException e) {
+ log.warn("Added child does not have parent, ignoring event.");
+ }
+ }
+
+ /**
+ * Invoked when some child has been removed.
+ */
+ private synchronized void childRemoved(String uuid, Path path) {
+ try {
+ PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
+ if (parent != null) {
+ PathMap.Child child = parent.removeChild(path.getNameElement());
+ if (child != null) {
+ zombieNodes.put(uuid, child);
+ }
+ }
+ } catch (PathNotFoundException e) {
+ log.warn("Added child does not have parent, ignoring event.");
+ }
+ }
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import org.apache.jackrabbit.core.SessionListener;
+import org.apache.jackrabbit.core.SessionImpl;
+
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+/**
+ * Lock token
+ */
+class LockToken {
+
+ /**
+ * UUID of node holding lock
+ */
+ public final String uuid;
+
+ /**
+ * Create a new instance of this class. Used when creating new locks upon
+ * a request.
+ * @param uuid uuid
+ */
+ public LockToken(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /**
+ * Parse a lock token string representation and return a lock token instance.
+ * @param s string representation of lock token
+ * @throws IllegalArgumentException if some field is illegal
+ */
+ public static LockToken parse(String s)
+ throws IllegalArgumentException {
+
+ int sep = s.lastIndexOf('-');
+ if (sep == -1 || sep == s.length() - 1) {
+ throw new IllegalArgumentException("Separator not found.");
+ }
+ String uuid = s.substring(0, sep);
+ if (getCheckDigit(uuid) != s.charAt(s.length() - 1)) {
+ throw new IllegalArgumentException("Bad check digit.");
+ }
+ return new LockToken(uuid);
+ }
+
+ /**
+ * Return the string representation of a lock token
+ * @return string representation
+ * @see #toString
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append(uuid);
+ buf.append('-');
+ buf.append(getCheckDigit(uuid));
+ return buf.toString();
+ }
+
+ /**
+ * Return the check digit for a lock token, given by its UUID
+ * @param uuid uuid
+ * @return check digit
+ */
+ private static char getCheckDigit(String uuid) {
+ int result = 0;
+
+ int multiplier = 36;
+ for (int i = 0; i < uuid.length(); i++) {
+ char c = uuid.charAt(i);
+ if (c >= '0' && c <= '9') {
+ int num = c - '0';
+ result += multiplier * num;
+ multiplier--;
+ } else if (c >= 'A' && c <= 'F') {
+ int num = c - 'A' + 10;
+ result += multiplier * num;
+ multiplier--;
+ } else if (c >= 'a' && c <= 'f') {
+ int num = c - 'a' + 10;
+ result += multiplier * num;
+ multiplier--;
+ }
+ }
+
+ int rem = 37 - (result % 37);
+ if (rem >= 0 && rem <= 9) {
+ return (char) ('0' + rem);
+ } else if (rem >= 10 && rem <= 35) {
+ return (char) ('A' + rem - 10);
+ } else {
+ return '+';
+ }
+ }
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockToken.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.lock;
+
+import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.QName;
+
+import java.util.*;
+
+/**
+ * Generic path map that associates information with the individual path elements
+ * of a path.
+ */
+public class PathMap {
+
+ /**
+ * Root element
+ */
+ private final Child root = new Child(null, Path.ROOT.getNameElement());
+
+ /**
+ * Map a path to a child. If <code>exact</code> is <code>false</code>,
+ * returns the last available item along the path that is stored in the map.
+ * @param path path to map
+ * @param exact flag indicating whether an exact match is required
+ * @return child, maybe <code>null</code> if <code>exact</code> is
+ * <code>true</code>
+ */
+ public Child map(Path path, boolean exact) {
+ Path.PathElement[] elements = path.getElements();
+ Child current = root;
+
+ for (int i = 1; i < elements.length; i++) {
+ Child next = current.getChild(elements[i], false);
+ if (next == null) {
+ if (exact) {
+ return null;
+ }
+ break;
+ }
+ current = next;
+ }
+ return current;
+ }
+
+ /**
+ * Create a child given by its path. The path map will create any necessary
+ * intermediate children.
+ * @param path path to child
+ * @param obj object to store at destination
+ */
+ public void put(Path path, Object obj) {
+ Path.PathElement[] elements = path.getElements();
+ Child current = root;
+
+ for (int i = 1; i < elements.length; i++) {
+ current = current.getChild(elements[i], true);
+ }
+ current.obj = obj;
+ }
+
+ /**
+ * Ressurrect a child previously removed, given by its new path and the
+ * child structure.
+ * @param path path to child
+ * @param zombie previously removed child object to store at destination
+ */
+ public void resurrect(Path path, Child zombie) {
+ Path.PathElement[] elements = path.getElements();
+ Child current = root;
+
+ for (int i = 1; i < elements.length; i++) {
+ current = current.getChild(elements[i], true);
+ }
+
+ current.children = zombie.children;
+ current.childrenCount = zombie.childrenCount;
+ current.obj = zombie.obj;
+ }
+
+ /**
+ * Traverse the path map and call back requester.
+ * @param includeEmpty if <code>true</code> invoke call back on every child
+ * regardless, whether the associated object is empty
+ * or not; otherwise call back on non-empty children
+ * only
+ */
+ public void traverse(ChildVisitor visitor, boolean includeEmpty) {
+ root.traverse(visitor, includeEmpty);
+ }
+
+ /**
+ * Internal class holding the object associated with a certain
+ * path element.
+ */
+ public static class Child {
+
+ /**
+ * Parent child
+ */
+ private final Child parent;
+
+ /**
+ * Map of immediate children of this child.
+ */
+ private Map children;
+
+ /**
+ * Number of non-null children
+ */
+ private int childrenCount;
+
+ /**
+ * Object associated with this child
+ */
+ private Object obj;
+
+ /**
+ * QName associated with this child
+ */
+ private QName name;
+
+ /**
+ * index associated with this child
+ */
+ private int index;
+
+ /**
+ * Create a new instance of this class.
+ * @param parent parent of this child
+ * @param element path element of this child
+ * @param obj associated object
+ */
+ Child(Child parent, Path.PathElement element, Object obj) {
+ this.parent = parent;
+ this.name = element.getName();
+ this.index = element.getIndex();
+ this.obj = obj;
+ }
+
+ /**
+ * Create a new instance of this class.
+ * @param parent parent of this child
+ * @param element path element of this child
+ */
+ Child(Child parent, Path.PathElement element) {
+ this(parent, element, null);
+ }
+
+ /**
+ * Insert an empty child. Will shift all children having an index
+ * greater than or equal to the child inserted to the right.
+ * @param element child's path element
+ */
+ public void insertChild(Path.PathElement element) {
+ int index = getOneBasedIndex(element) - 1;
+ if (children != null) {
+ ArrayList list = (ArrayList) children.get(element.getName());
+ if (list != null && list.size() > index) {
+ for (int i = index; i < list.size(); i++) {
+ Child child = (Child) list.get(i);
+ if (child != null) {
+ child.index++;
+ }
+ }
+ list.add(index, null);
+ }
+ }
+ }
+
+ /**
+ * Remove a child. Will shift all children having an index greater than
+ * the child removed to the left.
+ * @param element child's path element
+ * @return removed child, may be <code>null</code>
+ */
+ public Child removeChild(Path.PathElement element) {
+ int index = getOneBasedIndex(element) - 1;
+ if (children != null) {
+ ArrayList list = (ArrayList) children.get(element.getName());
+ if (list != null && list.size() > index) {
+ for (int i = index + 1; i < list.size(); i++) {
+ Child child = (Child) list.get(i);
+ if (child != null) {
+ child.index--;
+ }
+ }
+ Child child = (Child) list.remove(index);
+ if (child != null) {
+ childrenCount--;
+ }
+ if (childrenCount == 0) {
+ remove();
+ }
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return a child matching a path element. If a child doesn not exist
+ * at that position and <code>create</code> is <code>true</code> a
+ * new child will be created.
+ * @param element child's path element
+ * @param create flag indicating whether this child should be
+ * created if not available
+ */
+ private Child getChild(Path.PathElement element, boolean create) {
+ int index = getOneBasedIndex(element) - 1;
+ Child child = null;
+
+ if (children != null) {
+ ArrayList list = (ArrayList) children.get(element.getName());
+ if (list != null && list.size() > index) {
+ child = (Child) list.get(index);
+ }
+ }
+ if (child == null && create) {
+ if (children == null) {
+ children = new HashMap();
+ }
+ ArrayList list = (ArrayList) children.get(element.getName());
+ if (list == null) {
+ list = new ArrayList();
+ children.put(element.getName(), list);
+ }
+ while (list.size() < index) {
+ list.add(null);
+ }
+ child = new Child(this, element);
+ list.add(child);
+ childrenCount++;
+ }
+ return child;
+ }
+
+ /**
+ * Remove this child. Delegates the call to the parent item.
+ */
+ public void remove() {
+ if (parent != null) {
+ parent.removeChild(getPathElement());
+ }
+ }
+
+ /**
+ * Return the object associated with this child
+ * @return object associated with this child
+ */
+ public Object get() {
+ return obj;
+ }
+
+ /**
+ * Set the object associated with this child
+ * @param obj object associated with this child
+ */
+ public void set(Object obj) {
+ this.obj = obj;
+ }
+
+ /**
+ * Return a path element pointing to this child
+ * @return path element
+ */
+ public Path.PathElement getPathElement() {
+ return Path.create(name, index).getNameElement();
+ }
+
+ /**
+ * Checks whether this child has the specified path
+ * @return path path to compare to
+ */
+ public boolean hasPath(Path path) {
+ return hasPath(path.getElements(), path.getLength());
+ }
+
+ /**
+ * Checks whether this child has the specified path, given by
+ * path elements.
+ * @param elements path elements to compare to
+ * @param len number of elements to compare to
+ * @return <code>true</code> if this child has the path given;
+ * otherwise <code>false</code>
+ */
+ private boolean hasPath(Path.PathElement[] elements, int len) {
+ if (getPathElement().equals(elements[len - 1])) {
+ if (parent != null) {
+ return parent.hasPath(elements, len - 1);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return 1-based index of a path element.
+ */
+ private static int getOneBasedIndex(Path.PathElement element) {
+ int index = element.getIndex();
+ return index == 0 ? 1 : index;
+ }
+
+ /**
+ * Recursively invoked traversal method.
+ */
+ private void traverse(ChildVisitor visitor, boolean includeEmpty) {
+ if (children != null) {
+ Iterator iter = children.values().iterator();
+ while (iter.hasNext()) {
+ ArrayList list = (ArrayList) iter.next();
+ for (int i = 0; i < list.size(); i++) {
+ Child child = (Child) list.get(i);
+ if (child != null) {
+ child.traverse(visitor, includeEmpty);
+ }
+ }
+ }
+ }
+ if (includeEmpty || obj != null) {
+ visitor.childVisited(this);
+ }
+ }
+
+ }
+
+ /**
+ * Child visitor used in {@link PathMap#traverse}
+ */
+ public interface ChildVisitor {
+
+ /**
+ * Invoked for every child visited on a tree traversal
+ * @param child child visited
+ */
+ public void childVisited(Child child);
+ }
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/SerializationTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/SerializationTest.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/SerializationTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/SerializationTest.java Thu Mar 10 05:36:44 2005
@@ -154,10 +154,11 @@
public void testLockException() throws RepositoryException, IOException {
Repository repository = session.getRepository();
exportRepository(SKIPBINARY, RECURSE);
- if (repository.getDescriptor("OPTION_LOCKING_SUPPORTED") != null) {
+ if (repository.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED) != null) {
//A LockException is thrown if a lock prevents the addition of the subtree.
- Node lNode = testRootNode.addNode("lNode");
- lNode.addMixin("mix:lockable");
+ Node lNode = testRootNode.addNode(nodeName1);
+ lNode.addMixin(mixLockable);
+ testRootNode.save();
Lock lock = lNode.lock(true, true);
session.removeLockToken(lock.getLockToken()); //remove the token, so the lock is for me, too
FileInputStream in = new FileInputStream(file);
@@ -462,4 +463,4 @@
}
}
}
-}
\ No newline at end of file
+}
Added: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java?view=auto&rev=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java (added)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java Thu Mar 10 05:36:44 2005
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.test.api.lock;
+
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.lock.LockException;
+import javax.jcr.lock.Lock;
+
+/**
+ * <code>LockTest</code> contains the test cases for the methods
+ * inside ...
+ *
+ * @test
+ * @sources XATest.java
+ * @executeClass org.apache.jackrabbit.test.api.xa.XATest
+ * @keywords level2
+ */
+public class LockTest extends AbstractJCRTest {
+
+ /**
+ * @see junit.framework#runTest
+ *
+ * Make sure that tested repository supports locking
+ */
+ protected void runTest() throws Throwable {
+ Repository rep = helper.getRepository();
+ if (rep.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED) != null) {
+ super.runTest();
+ }
+ }
+
+ /**
+ * Test session scope: other session may not access nodes that are
+ * locked.
+ * @throws Exception
+ */
+ public void testSessionScope() throws Exception {
+ // create new node and lock it
+ Node n = testRootNode.addNode(nodeName1, testNodeType);
+ n.addMixin(mixReferenceable);
+ n.addMixin(mixLockable);
+ testRootNode.save();
+
+ // remember uuid
+ String uuid = n.getUUID();
+
+ // lock node
+ Lock lock = n.lock(false, true);
+
+ // assertion: isLive must return true
+ assertTrue("Lock must be live", lock.isLive());
+
+ // assertion: lock token must not be null for holding session
+ String lockToken = n.getLock().getLockToken();
+ assertNotNull("Lock token must not be null for holding session", lockToken);
+
+ // create new session
+ Session otherSuperuser = helper.getSuperuserSession();
+
+ // get same node
+ n = otherSuperuser.getNodeByUUID(uuid);
+
+ // assertion: lock token must be null for other session
+ assertNull("Lock token must be null for other session",
+ n.getLock().getLockToken());
+
+ // assertion: modifying same node in other session must fail
+ try {
+ n.addNode(nodeName2);
+ fail("Modified node locked by other session.");
+ } catch (LockException e) {
+ // expected
+ }
+
+ // logout
+ otherSuperuser.logout();
+
+ // unlock node
+ superuser.getNodeByUUID(uuid).unlock();
+
+ // assertion: isLive must return false
+ assertFalse("Lock must be dead", lock.isLive());
+ }
+}
+
Propchange: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/TestAll.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/TestAll.java?view=diff&r1=156948&r2=156949
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/TestAll.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/TestAll.java Thu Mar 10 05:36:44 2005
@@ -36,8 +36,8 @@
public static Test suite() {
TestSuite suite = new TestSuite("javax.jcr.lock tests");
- // ADD TEST CLASSES HERE:
+ suite.addTestSuite(LockTest.class);
return suite;
}
-}
\ No newline at end of file
+}
|