jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject svn commit: r156949 - in incubator/jackrabbit/trunk/src: java/org/apache/jackrabbit/core/ java/org/apache/jackrabbit/core/lock/ test/org/apache/jackrabbit/test/api/ test/org/apache/jackrabbit/test/api/lock/
Date Thu, 10 Mar 2005 13:37:21 GMT
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
+}



Mime
View raw message