jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r157083 - in incubator/jackrabbit/trunk/src: java/org/apache/jackrabbit/core/ java/org/apache/jackrabbit/core/lock/ java/org/apache/jackrabbit/core/version/ java/org/apache/jackrabbit/core/version/persistence/ test/org/apache/jackrabbit/test/api/lock/
Date Fri, 11 Mar 2005 14:26:31 GMT
Author: tripod
Date: Fri Mar 11 06:26:27 2005
New Revision: 157083

URL: http://svn.apache.org/viewcvs?view=rev&rev=157083
Log:
- fixing latest locking issues
- fixing version items refresh

Added:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
  (with props)
Modified:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.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/SessionListener.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
    incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java

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=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java Fri Mar 11
06:26:27 2005
@@ -523,7 +523,7 @@
         removeChildProperty(qName);
     }
 
-    protected void removeChildProperty(QName propName) throws RepositoryException {
+    public void removeChildProperty(QName propName) throws RepositoryException {
         // modify the state of 'this', i.e. the parent node
         NodeState thisState = (NodeState) getOrCreateTransientItemState();
 
@@ -3645,22 +3645,7 @@
         checkLockable();
 
         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;
+        return lockMgr.lock(this, isDeep, isSessionScoped);
     }
 
     /**
@@ -3699,10 +3684,6 @@
 
         LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
         lockMgr.unlock(this);
-
-        removeChildProperty(JCR_LOCKOWNER);
-        removeChildProperty(JCR_LOCKISDEEP);
-        save();
     }
 
     /**

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=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java Fri
Mar 11 06:26:27 2005
@@ -779,6 +779,11 @@
     /**
      * {@inheritDoc}
      */
+    public void loggingOut(SessionImpl session) {}
+
+    /**
+     * {@inheritDoc}
+     */
     public void loggedOut(SessionImpl session) {
         // remove session from active sessions
         activeSessions.remove(session);

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=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java Fri Mar
11 06:26:27 2005
@@ -409,6 +409,24 @@
     }
 
     /**
+     * Notify the listeners that this session is about to be closed.
+     */
+    protected void notifyLoggingOut() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        SessionListener[] la = new SessionListener[listeners.size()];
+        Iterator iter = listeners.values().iterator();
+        int cnt = 0;
+        while (iter.hasNext()) {
+            la[cnt++] = (SessionListener) iter.next();
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].loggingOut(this);
+            }
+        }
+    }
+
+    /**
      * Notify the listeners that this session has been closed.
      */
     protected void notifyLoggedOut() {
@@ -1023,6 +1041,9 @@
             // ignore
             return;
         }
+
+        // notify listeners that session is about to be closed
+        notifyLoggingOut();
 
         // discard all transient changes
         itemStateMgr.disposeAllTransientItemStates();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java Fri
Mar 11 06:26:27 2005
@@ -25,6 +25,15 @@
 public interface SessionListener {
 
     /**
+     * Called when a <code>Session</code> is about to be 'closed' by
+     * calling <code>{@link javax.jcr.Session#logout()}</code. At this
+     * moment the session is still valid.
+     *
+     * @param session the <code>Session</code> that is about to be 'closed'
+     */
+    public void loggingOut(SessionImpl session);
+
+    /**
      * Called when a <code>Session</code> has been 'closed' by
      * calling <code>{@link javax.jcr.Session#logout()}</code.
      *

Modified: 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=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java Fri
Mar 11 06:26:27 2005
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.core.lock;
 
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.NodeId;
+
 import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.Node;
@@ -35,7 +38,7 @@
     /**
      * Node holding lock
      */
-    private Node node;
+    private final Node node;
 
     /**
      * Create a new instance of this class.
@@ -92,6 +95,12 @@
      * {@inheritDoc}
      */
     public void refresh() throws LockException, RepositoryException {
-        //@todo implement refresh
+        if (isLive()) {
+            throw new LockException("Lock still alive.");
+        }
+        if (getLockToken() == null) {
+            throw new LockException("Session does not hold lock.");
+        }
+        info.refresh((NodeImpl) node);
     }
 }

Modified: 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=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java Fri
Mar 11 06:26:27 2005
@@ -18,8 +18,13 @@
 
 import org.apache.jackrabbit.core.SessionListener;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.log4j.Logger;
 
 import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
 
 /**
  * Contains information about a lock and gets placed inside the child
@@ -28,6 +33,16 @@
 class LockInfo implements SessionListener {
 
     /**
+     * Logger
+     */
+    private static final Logger log = Logger.getLogger(LockInfo.class);
+
+    /**
+     * Lock manager
+     */
+    private final LockManagerImpl lockMgr;
+
+    /**
      * Lock token
      */
     final LockToken lockToken;
@@ -59,18 +74,19 @@
 
     /**
      * Create a new instance of this class.
+     * @param lockMgr lock manager
      * @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) {
+    public LockInfo(LockManagerImpl lockMgr, LockToken lockToken,
+                    boolean sessionScoped, boolean deep, String lockOwner) {
+        this.lockMgr = lockMgr;
         this.lockToken = lockToken;
         this.sessionScoped = sessionScoped;
         this.deep = deep;
         this.lockOwner = lockOwner;
-        this.live = true;
     }
 
     /**
@@ -111,7 +127,7 @@
      * itself, otherwise a <code>null</code> string
      */
     public String getLockToken(Session session) {
-        if (live && session.equals(lockHolder)) {
+        if (session.equals(lockHolder)) {
             return lockToken.toString();
         }
         return null;
@@ -125,12 +141,34 @@
         return live;
     }
 
+    /**
+     * Refresh a lock. Will try to re-insert this lock info into the lock
+     * manager's path map, provided the node is not already locked.
+     * @param node node to lock again
+     * @throws LockException if the node is already locked
+     * @throws RepositoryException if some other error occurs
+     * @see javax.jcr.Node#refresh
+     */
+    public void refresh(NodeImpl node) throws LockException, RepositoryException {
+        lockMgr.lock(node, this);
+    }
+
     //-------------------------------------------------------< SessionListener >
 
     /**
      * {@inheritDoc}
-     */
-    public void loggedOut(SessionImpl session) {
-        live = false;
+     *
+     * When the owning session is logging out, we should unlock the node on
+     * behalf of the user currently holding it.
+     */
+    public void loggingOut(SessionImpl session) {
+        if (live) {
+            lockMgr.unlock(this);
+        }
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void loggedOut(SessionImpl session) {}
 }

Modified: 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=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
Fri Mar 11 06:26:27 2005
@@ -145,9 +145,10 @@
             NodeImpl node = (NodeImpl) session.getNodeByUUID(lockToken.uuid);
             Path path = node.getPrimaryPath();
 
-            LockInfo info = new LockInfo(lockToken, false,
+            LockInfo info = new LockInfo(this, lockToken, false,
                     node.getProperty(Constants.JCR_LOCKISDEEP).getBoolean(),
                     node.getProperty(Constants.JCR_LOCKOWNER).getString());
+            info.setLive(true);
             lockMap.put(path, info);
         } catch (RepositoryException e) {
             log.warn("Unable to recreate lock '" + lockToken +
@@ -192,35 +193,46 @@
         }
     }
 
-    //-----------------------------------------------------------< LockManager >
-
     /**
-     * {@inheritDoc}
+     * Internal <code>lock</code> implementation that takes as parameter
+     * a lock info that will be used inside the path map.
+     * @param node node to lock
+     * @param info lock info
+     * @throws LockException if the node is already locked
+     * @throws RepositoryException if another error occurs
+     * @return lock
      */
-    public synchronized Lock lock(NodeImpl node, boolean isDeep,
-                                  boolean isSessionScoped)
-            throws LockException, RepositoryException {
+    Lock lock(NodeImpl node, LockInfo info)
+            throws LockException,  RepositoryException {
 
+        // check whether node is already locked
         Path path = node.getPrimaryPath();
         PathMap.Child child = lockMap.map(path, false);
 
-        LockInfo info = (LockInfo) child.get();
-        if (info != null) {
+        LockInfo other = (LockInfo) child.get();
+        if (other != null) {
             if (child.hasPath(path)) {
                 throw new LockException("Node already locked: " + node.safeGetJCRPath());
-            } else if (info.deep) {
+            } else if (other.deep) {
                 throw new LockException("Parent node has deep lock.");
             }
         }
-        if (isDeep && child.hasPath(path)) {
+        if (info.deep && child.hasPath(path)) {
             throw new LockException("Some child node is locked.");
         }
 
+        // add properties to content
+        node.internalSetProperty(Constants.JCR_LOCKOWNER,
+                InternalValue.create(node.getSession().getUserId()));
+        node.internalSetProperty(Constants.JCR_LOCKISDEEP,
+                InternalValue.create(info.deep));
+        node.save();
+
+        // create lock token
         SessionImpl session = (SessionImpl) node.getSession();
-        info = new LockInfo(new LockToken(node.internalGetUUID()),
-                isSessionScoped, isDeep, session.getUserId());
         info.setLockHolder(session);
-        if (isSessionScoped) {
+        info.setLive(true);
+        if (info.sessionScoped) {
             session.addListener(info);
         }
         session.addLockToken(info.lockToken.toString(), false);
@@ -229,6 +241,60 @@
     }
 
     /**
+     * Unlock a node given by its info. Invoked when a session logs out and
+     * all session scoped locks of that session must be unlocked.
+     * @param info lock info
+     */
+    void unlock(LockInfo info) {
+        // if no session currently holds lock, take system session
+        SessionImpl session = info.getLockHolder();
+        if (session == null) {
+            session = this.session;
+        }
+
+        try {
+            // get node's path and remove child in path map
+            NodeImpl node = (NodeImpl) session.getItemManager().getItem(
+                    new NodeId(info.getUUID()));
+            Path path = node.getPrimaryPath();
+
+            PathMap.Child child = lockMap.map(path, true);
+            if (child != null) {
+                child.set(null);
+            }
+
+            // set live flag to false
+            info.setLive(false);
+
+            // remove properties in content
+            node.removeChildProperty(Constants.JCR_LOCKOWNER);
+            node.removeChildProperty(Constants.JCR_LOCKISDEEP);
+            node.save();
+            
+        } catch (RepositoryException e) {
+            log.warn("Unable to unlock session-scoped lock on node '" +
+                    info.lockToken + "': " + e.getMessage());
+            log.debug("Root cause: ", e);
+        }
+
+    }
+
+    //-----------------------------------------------------------< LockManager >
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized Lock lock(NodeImpl node, boolean isDeep,
+                                  boolean isSessionScoped)
+            throws LockException, RepositoryException {
+
+        // create lock info to use and pass to internal implementation
+        LockInfo info = new LockInfo(this, new LockToken(node.internalGetUUID()),
+                isSessionScoped, isDeep, node.getSession().getUserId());
+        return lock(node, info);
+    }
+
+    /**
      * {@inheritDoc}
      */
     public synchronized Lock getLock(NodeImpl node)
@@ -254,6 +320,7 @@
     public synchronized void unlock(NodeImpl node)
             throws LockException, RepositoryException {
 
+        // check whether node is locked by this session
         Path path = node.getPrimaryPath();
 
         PathMap.Child child = lockMap.map(path, true);
@@ -268,11 +335,15 @@
         if (!node.getSession().equals(info.getLockHolder())) {
             throw new LockException("Node not locked by session: " + node.safeGetJCRPath());
         }
-        child.set(null);
 
+        // remove lock in path map
+        child.set(null);
         info.setLive(false);
-        info.setLockHolder(null);
-        session.removeLockToken(info.lockToken.toString(), false);
+
+        // remove properties in content
+        node.removeChildProperty(Constants.JCR_LOCKOWNER);
+        node.removeChildProperty(Constants.JCR_LOCKISDEEP);
+        node.save();
     }
 
     /**
@@ -399,7 +470,7 @@
             switch (event.getType()) {
                 case Event.NODE_ADDED:
                     try {
-                        childAdded(event.getChildUUID(),
+                        nodeAdded(event.getChildUUID(),
                                 Path.create(event.getPath(), nsResolver, true));
                     } catch (MalformedPathException e) {
                         log.info("Unable to get event's path: " + e.getMessage());
@@ -409,7 +480,7 @@
                     break;
                 case Event.NODE_REMOVED:
                     try {
-                        childRemoved(event.getChildUUID(),
+                        nodeRemoved(event.getChildUUID(),
                                 Path.create(event.getPath(), nsResolver, true));
                     } catch (MalformedPathException e) {
                         log.info("Unable to get event's path: " + e.getMessage());
@@ -422,9 +493,11 @@
     }
 
     /**
-     * Invoked when some child has been added.
+     * Invoked when some node has been added. Relink the child inside our
+     * zombie map to the new parent. Revitalize all locks inside the
+     * zombie child hierarchy.
      */
-    private synchronized void childAdded(String uuid, Path path) {
+    private synchronized void nodeAdded(String uuid, Path path) {
         try {
             PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
             if (parent != null) {
@@ -432,27 +505,41 @@
             }
             PathMap.Child zombie = (PathMap.Child) zombieNodes.remove(uuid);
             if (zombie != null) {
+                zombie.traverse(new PathMap.ChildVisitor() {
+                    public void childVisited(PathMap.Child child) {
+                        LockInfo info = (LockInfo) child.get();
+                        info.setLive(true);
+                    }
+                }, false);
                 lockMap.resurrect(path, zombie);
             }
         } catch (PathNotFoundException e) {
-            log.warn("Added child does not have parent, ignoring event.");
+            log.warn("Added node does not have parent, ignoring event.");
         }
     }
 
     /**
-     * Invoked when some child has been removed.
+     * Invoked when some node has been removed. Unlink the child inside
+     * our path map corresponding to that node. Disable all locks contained
+     * in that subtree.
      */
-    private synchronized void childRemoved(String uuid, Path path) {
+    private synchronized void nodeRemoved(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) {
+                    child.traverse(new PathMap.ChildVisitor() {
+                        public void childVisited(PathMap.Child child) {
+                            LockInfo info = (LockInfo) child.get();
+                            info.setLive(false);
+                        }
+                    }, false);
                     zombieNodes.put(uuid, child);
                 }
             }
         } catch (PathNotFoundException e) {
-            log.warn("Added child does not have parent, ignoring event.");
+            log.warn("Removed node does not have parent, ignoring event.");
         }
     }
 }

Modified: 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=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java Fri Mar
11 06:26:27 2005
@@ -203,7 +203,7 @@
                     if (child != null) {
                         childrenCount--;
                     }
-                    if (childrenCount == 0) {
+                    if (obj == null && childrenCount == 0) {
                         remove();
                     }
                     return child;
@@ -323,7 +323,7 @@
         /**
          * Recursively invoked traversal method.
          */
-        private void traverse(ChildVisitor visitor, boolean includeEmpty) {
+        public void traverse(ChildVisitor visitor, boolean includeEmpty) {
             if (children != null) {
                 Iterator iter = children.values().iterator();
                 while (iter.hasNext()) {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
Fri Mar 11 06:26:27 2005
@@ -37,4 +37,11 @@
      * @return
      */
     public InternalVersionItem getParent();
+
+    /**
+     * Adds a vesion item state listener to this item
+     *
+     * @param listener
+     */
+    public void addListener(InternalVersionItemListener listener);
 }

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java?view=auto&rev=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
(added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
Fri Mar 11 06:26:27 2005
@@ -0,0 +1,34 @@
+/*
+ * 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.version;
+
+/**
+ * This Interface defines a listener on versionitem events
+ *
+ * @author tripod
+ * @version $Revision:$, $Date:$
+ */
+public interface InternalVersionItemListener {
+
+    /**
+     * is called, when the version item was modified
+     *
+     * @param item
+     */
+    public void itemModifed(InternalVersionItem item);
+
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
Fri Mar 11 06:26:27 2005
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.core.QName;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.UnknownPrefixException;
+import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.core.state.NodeState;
 
 import javax.jcr.Item;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
Fri Mar 11 06:26:27 2005
@@ -159,4 +159,6 @@
      * @throws Exception if an error occurs
      */
     public void close() throws Exception;
+
+
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
Fri Mar 11 06:26:27 2005
@@ -18,6 +18,7 @@
 
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.Constants;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.ItemStateManager;
@@ -36,7 +37,7 @@
  * version storage ({@link VersionItemStateProvider}) and the persistent
  * version manager.
  */
-public class VersionManagerImpl implements VersionManager {
+public class VersionManagerImpl implements VersionManager, Constants, InternalVersionItemListener
{
 
     /**
      * the default logger
@@ -122,11 +123,8 @@
      */
     public VersionHistory createVersionHistory(NodeImpl node) throws RepositoryException
{
         InternalVersionHistory history = vMgr.createVersionHistory(node);
-        try {
-            virtProvider.getItemState(new NodeId(VERSION_STORAGE_NODE_UUID)).discard();
-        } catch (ItemStateException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings
| File Templates.
-        }
+        history.addListener(this);
+        onVersionStorageChanged();
         return (VersionHistory) node.getSession().getNodeByUUID(history.getId());
     }
 
@@ -148,7 +146,11 @@
      * @throws RepositoryException
      */
     public InternalVersionHistory getVersionHistory(String id) throws RepositoryException
{
-        return vMgr.getVersionHistory(id);
+        InternalVersionHistory hist = vMgr.getVersionHistory(id);
+        if (hist != null) {
+            hist.addListener(this);
+        }
+        return hist;
     }
 
     /**
@@ -189,7 +191,11 @@
      * @throws RepositoryException
      */
     public InternalVersion getVersion(String id) throws RepositoryException {
-        return vMgr.getVersion(id);
+        InternalVersion vers = vMgr.getVersion(id);
+        if (vers != null) {
+            vers.addListener(this);
+        }
+        return vers;
     }
 
     /**
@@ -210,7 +216,11 @@
      * @throws RepositoryException
      */
     public InternalVersionItem getItem(String id) throws RepositoryException {
-        return vMgr.getItemByExternal(id);
+        InternalVersionItem item = vMgr.getItemByExternal(id);
+        if (item != null) {
+            item.addListener(this);
+        }
+        return item;
     }
 
     /**
@@ -223,11 +233,7 @@
      */
     public Version checkin(NodeImpl node) throws RepositoryException {
         InternalVersion version = vMgr.checkin(node);
-        try {
-            virtProvider.getItemState(new NodeId(version.getVersionHistory().getId())).discard();
-        } catch (ItemStateException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings
| File Templates.
-        }
+        version.addListener(this);
         return (Version) node.getSession().getNodeByUUID(version.getId());
     }
 
@@ -243,5 +249,27 @@
      */
     public void setItemReferences(InternalVersionItem item, List references) {
         vMgr.setItemReferences(item, references);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void itemModifed(InternalVersionItem item) {
+        try {
+            virtProvider.getItemState(new NodeId(item.getId())).discard();
+        } catch (ItemStateException e) {
+            log.error("Error while refreshing virtual item.", e);
+        }
+    }
+
+    /**
+     * Flushes the virtual node state information of the version storage
+     */
+    public void onVersionStorageChanged() {
+        try {
+            virtProvider.getItemState(new NodeId(VERSION_STORAGE_NODE_UUID)).discard();
+        } catch (ItemStateException e) {
+            log.error("Error while refreshing virtual version storage.", e);
+        }
     }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
Fri Mar 11 06:26:27 2005
@@ -273,6 +273,7 @@
 
                 stateMgr.update();
                 succeeded = true;
+                notifyModifed();
             } finally {
                 if (!succeeded) {
                     // update operation failed, cancel all modifications
@@ -331,6 +332,7 @@
                 stateMgr.cancel();
             }
         }
+        notifyModifed();
         return prev;
     }
 
@@ -363,7 +365,7 @@
                 stateMgr.cancel();
             }
         }
-
+        notifyModifed();
         return v;
     }
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
Fri Mar 11 06:26:27 2005
@@ -18,11 +18,16 @@
 
 import org.apache.jackrabbit.core.version.InternalVersionItem;
 import org.apache.jackrabbit.core.version.PersistentVersionManager;
+import org.apache.jackrabbit.core.version.InternalVersionItemListener;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Iterator;
 
 /**
  *
  */
-abstract class InternalVersionItemImpl {
+abstract class InternalVersionItemImpl implements InternalVersionItem {
 
     /**
      * the version manager
@@ -30,6 +35,11 @@
     private final PersistentVersionManager vMgr;
 
     /**
+     * the item listeners
+     */
+    private final Set listeners = new HashSet();
+
+    /**
      * Creates a new Internal version item impl
      *
      * @param vMgr
@@ -45,6 +55,23 @@
      */
     protected PersistentVersionManager getVersionManager() {
         return vMgr;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addListener(InternalVersionItemListener listener) {
+        listeners.add(listener);
+    }
+
+    /**
+     * notifys the listeners that this item was modified
+     */
+    protected void notifyModifed() {
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            ((InternalVersionItemListener) iter.next()).itemModifed(this);
+        }
     }
 
     /**

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
Fri Mar 11 06:26:27 2005
@@ -563,6 +563,9 @@
 
             initVirtualIds(v.getId(), v.getNode().getState());
 
+            // notify listeners
+            history.notifyModifed();
+
             return v;
         } catch (ItemStateException e) {
             throw new RepositoryException(e);

Modified: 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=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
(original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
Fri Mar 11 06:26:27 2005
@@ -42,59 +42,98 @@
 public class LockTest extends AbstractJCRTest {
 
     /**
+     * Test lock token functionality
+     */
+    public void testAddRemoveLockToken() throws Exception {
+        // create new node
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixReferenceable);
+        n.addMixin(mixLockable);
+        testRootNode.save();
+
+        // lock node and get lock token
+        Lock lock = n.lock(false, true);
+
+        // assert: session must get a non-null lock token
+        assertNotNull("session must get a non-null lock token",
+                lock.getLockToken());
+
+        // assert: session must hold lock token
+        assertTrue("session must hold lock token",
+                containsLockToken(superuser, lock.getLockToken()));
+
+        // remove lock token
+        String lockToken = lock.getLockToken();
+        superuser.removeLockToken(lockToken);
+
+        // assert: session must get a null lock token
+        assertNull("session must get a null lock token",
+                lock.getLockToken());
+
+        // assert: session must still hold lock token
+        assertFalse("session must not hold lock token",
+                containsLockToken(superuser, lockToken));
+
+        // assert: session unable to modify node
+        try {
+            n.addNode(nodeName2, testNodeType);
+            fail("session unable to modify node");
+        } catch (LockException e) {
+            // expected
+        }
+
+        // add lock token
+        superuser.addLockToken(lockToken);
+
+        // assert: session must get a non-null lock token
+        assertNotNull("session must get a non-null lock token",
+                lock.getLockToken());
+
+        // assert: session must hold lock token
+        assertTrue("session must hold lock token",
+                containsLockToken(superuser, lock.getLockToken()));
+
+        // assert: session able to modify node
+        n.addNode(nodeName2, testNodeType);
+    }
+
+    /**
      * Test session scope: other session may not access nodes that are
      * locked.
      */
-    public void testSessionScope() throws Exception {
+    public void testNodeLocked() throws Exception {
         // create new node and lock it
-        Node n = testRootNode.addNode(nodeName1, testNodeType);
-        if (!n.isNodeType(mixReferenceable)) {
-            n.addMixin(mixReferenceable);
-        }
-        if (!n.isNodeType(mixLockable)) {
-            n.addMixin(mixLockable);
-        }
+        Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+        n1.addMixin(mixReferenceable);
+        n1.addMixin(mixLockable);
         testRootNode.save();
 
-        // remember uuid
-        String uuid = n.getUUID();
-
         // lock node
-        Lock lock = n.lock(false, true);
+        Lock lock = n1.lock(false, true);
 
-        // assertion: isLive must return true
+        // assert: 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);
+        Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
 
-        // assertion: lock token must be null for other session
+        // assert: lock token must be null for other session
         assertNull("Lock token must be null for other session",
-                n.getLock().getLockToken());
+                n2.getLock().getLockToken());
 
-        // assertion: modifying same node in other session must fail
+        // assert: modifying same node in other session must fail
         try {
-            n.addNode(nodeName2);
-            fail("Modified node locked by other session.");
+            n2.addNode(nodeName2, testNodeType);
+            fail("modifying same node in other session must fail");
         } 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());
     }
 
     /**
@@ -139,14 +178,168 @@
         // lock child node
         n2.lock(false, true);
 
-        // lock parent node
-        n1.lock(true, false);
+        // assert: unable to deep lock parent node
+        try {
+            n1.lock(true, true);
+            fail("unable to deep lock parent node");
+        } catch (LockException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test locks are released when session logs out
+     */
+    public void testLogout() throws Exception {
+        // add node
+        Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+        n1.addMixin(mixReferenceable);
+        n1.addMixin(mixLockable);
+        testRootNode.save();
+
+        // create new session
+        Session otherSuperuser = helper.getSuperuserSession();
+
+        // get node created above
+        Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
+
+        // lock node
+        Lock lock = n2.lock(false, true);
+
+        // assert: lock must be alive
+        assertTrue("lock must be alive", lock.isLive());
+
+        // assert: node must be locked
+        assertTrue("node must be locked", n1.isLocked());
+
+        // log out
+        otherSuperuser.logout();
+
+        // assert: lock must not be alive
+        assertFalse("lock must not be alive", lock.isLive());
+
+        // assert: node must not be locked
+        assertFalse("node must not be locked", n1.isLocked());
+    }
+
+    /**
+     * Test locks may be transferred to other session
+     */
+    public void testLockTransfer() throws Exception {
+        // add node
+        Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+        n1.addMixin(mixReferenceable);
+        n1.addMixin(mixLockable);
+        testRootNode.save();
+
+        // create new session
+        Session otherSuperuser = helper.getSuperuserSession();
 
-        // unlock child node
-        n2.unlock();
+        // get node created above
+        Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
 
-        // parent node must still hold lock
-        assertTrue("parent node must still hold lock", n1.holdsLock());
+        // lock node
+        Lock lock = n2.lock(false, true);
+
+        // assert: user must get non-null token
+        assertNotNull("user must get non-null token", lock.getLockToken());
+
+        // transfer to standard session
+        String lockToken = lock.getLockToken();
+        otherSuperuser.removeLockToken(lockToken);
+        superuser.addLockToken(lockToken);
+
+        // assert: user must get null token
+        assertNull("user must get null token", lock.getLockToken());
+
+        // assert: user must get non-null token
+        assertNotNull("user must get non-null token",
+                n1.getLock().getLockToken());
+
+        // log out
+        otherSuperuser.logout();
+    }
+
+    /**
+     * Test open-scoped locks
+     */
+    public void testOpenScopedLocks() throws Exception {
+        // add node
+        Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+        n1.addMixin(mixReferenceable);
+        n1.addMixin(mixLockable);
+        testRootNode.save();
+
+        // create new session
+        Session otherSuperuser = helper.getSuperuserSession();
+
+        // get node created above
+        Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
+
+        // lock node
+        Lock lock = n2.lock(false, false);
+
+        // transfer to standard session
+        String lockToken = lock.getLockToken();
+        otherSuperuser.removeLockToken(lockToken);
+        superuser.addLockToken(lockToken);
+
+        // log out
+        otherSuperuser.logout();
+
+        // assert: node still locked
+        assertTrue(n1.isLocked());
+    }
+
+    /**
+     * Test refresh
+     */
+    public void testRefresh() throws Exception {
+        // create new node
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixReferenceable);
+        n.addMixin(mixLockable);
+        testRootNode.save();
+
+        // lock node and get lock token
+        Lock lock = n.lock(false, true);
+
+        // assert: lock must be alive
+        assertTrue("lock must be alive", lock.isLive());
+
+        // assert: refresh must fail, since lock is still alive
+        try {
+            lock.refresh();
+            fail("refresh must fail, since lock is still alive");
+        } catch (LockException e) {
+            // expected
+        }
+
+        // unlock node
+        n.unlock();
+
+        // assert: lock must not be alive
+        assertFalse("lock must not be alive", lock.isLive());
+
+        // refresh
+        lock.refresh();
+
+        // assert: lock must again be alive
+        assertTrue("lock must again be alive", lock.isLive());
+    }
+
+    /**
+     * Return a flag indicating whether the indicated session contains
+     * a specific lock token
+     */
+    private boolean containsLockToken(Session session, String lockToken) {
+        String[] lt = session.getLockTokens();
+        for (int i = 0; i < lt.length; i++) {
+            if (lt[i].equals(lockToken)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
 



Mime
View raw message