Index: jackrabbit/src/test/java/org/apache/jackrabbit/test/api/lock/LockTest.java =================================================================== --- jackrabbit/src/test/java/org/apache/jackrabbit/test/api/lock/LockTest.java (revision 419486) +++ jackrabbit/src/test/java/org/apache/jackrabbit/test/api/lock/LockTest.java (working copy) @@ -438,6 +438,58 @@ } /** + * Test transfering a lock multiple times + */ + public void testMultipleLockTransfer() throws Exception { + // add node + Node n1 = testRootNode.addNode(nodeName1, testNodeType); + n1.addMixin(mixLockable); + testRootNode.save(); + + // create new session + Session session1 = null; + Session session2 = null; + Session session3 = null; + try { + // session 1 - locks the node + session1 = helper.getSuperuserSession(); + Node node1 = (Node) session1.getItem(n1.getPath()); + + Lock lock1 = node1.lock(false, false); + assertNotNull("session 1 must get a non-null token", lock1.getLockToken()); + + String lockToken = lock1.getLockToken(); + assertSame(session1, lock1.getNode().getSession()); + session1.logout(); + + // session2 - acquires the lock created by session 1 + session2 = helper.getSuperuserSession(); + session2.addLockToken(lockToken); + + Node node2 = (Node) session2.getItem(n1.getPath()); + Lock lock2 = node2.getLock(); + assertNotNull("session 2 must get a non-null token", lock2.getLockToken()); + assertNull("session 1 must get a null token", lock1.getLockToken()); + session2.logout(); + + // session3 - acquires the lock created by session 1 + session3 = helper.getSuperuserSession(); + session3.addLockToken(lockToken); + + Node node3 = (Node) session3.getItem(n1.getPath()); + Lock lock3 = node3.getLock(); + assertNotNull("session 3 must get a non-null token", lock3.getLockToken()); + assertNull("session 2 must get a null token", lock2.getLockToken()); + node3.unlock(); + } + finally { + if (session1 != null) session1.logout(); + if (session2 != null) session2.logout(); + if (session3 != null) session3.logout(); + } + } + + /** * Test open-scoped locks */ public void testOpenScopedLocks() throws Exception { Index: jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java =================================================================== --- jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (revision 419486) +++ jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (working copy) @@ -498,6 +498,7 @@ if (info != null) { if (info.getLockHolder() == null) { info.setLockHolder(session); + session.addListener(info); } else { log.warn("Adding lock token has no effect: " + "lock already held by other session."); @@ -527,6 +528,7 @@ if (info != null) { if (session.equals(info.getLockHolder())) { info.setLockHolder(null); + session.removeListener(info); } else { log.warn("Removing lock token has no effect: " + "lock held by other session."); @@ -902,11 +904,5 @@ } } } - - /** - * {@inheritDoc} - */ - public void loggedOut(SessionImpl session) { - } } } Index: jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java =================================================================== --- jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java (revision 419486) +++ jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java (working copy) @@ -18,13 +18,14 @@ import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.SessionListener; import javax.jcr.Session; /** * Common information about a lock. */ -abstract class AbstractLockInfo { +abstract class AbstractLockInfo implements SessionListener { /** * Lock token @@ -143,4 +144,16 @@ public boolean isSessionScoped() { return sessionScoped; } + + /** + * {@inheritDoc} + */ + public void loggingOut(SessionImpl session) { + } + + /** + * {@inheritDoc} + */ + public void loggedOut(SessionImpl session) { + } }