### Eclipse Workspace Patch 1.0 #P jackrabbit-core Index: src/test/java/org/apache/jackrabbit/core/lock/TestAll.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/lock/TestAll.java (revision 1033877) +++ src/test/java/org/apache/jackrabbit/core/lock/TestAll.java (working copy) @@ -35,7 +35,10 @@ public static Test suite() { TestSuite suite = new TestSuite("org.apache.jackrabbit.core.lock tests"); + suite.addTestSuite(ConcurrentLockingTest.class); + suite.addTestSuite(ConcurrentLockingWithTransactionsTest.class); suite.addTestSuite(ExtendedLockingTest.class); + suite.addTestSuite(LockTimeoutTest.class); return suite; } Index: src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java (revision 1033877) +++ src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java (working copy) @@ -63,7 +63,7 @@ private final long timeoutHint; /** - * Time (in seconds since epoch) when this lock will timeout. Set to + * Time (in milliseconds since the epoch) when this lock will timeout. Set to * {@link Long#MAX_VALUE} if this lock will not timeout. */ private long timeoutTime; @@ -128,7 +128,7 @@ /** * Return the lock owner. - * + * * @return lock owner */ public String getLockOwner() { @@ -137,7 +137,7 @@ /** * Return a flag indicating whether the lock is deep. - * + * * @return true if the lock is deep; * false otherwise */ @@ -146,7 +146,7 @@ } /** - * Return a flag indicating whether the session given is lock holder. + * Return a flag indicating whether the session given is lock holder. * * @param session session to compare with */ @@ -219,9 +219,9 @@ } /** - * Returns the time when this lock will expire. + * Returns the time when this lock will expire. * - * @return timeout time in seconds after epoch + * @return timeout time in milliseconds since the epoch */ public long getTimeoutTime() { return timeoutTime; @@ -229,7 +229,7 @@ public boolean isExpired() { return timeoutTime != Long.MAX_VALUE - && timeoutTime * 1000 > System.currentTimeMillis(); + && System.currentTimeMillis() > timeoutTime; } /** @@ -239,8 +239,8 @@ */ public void updateTimeoutTime() { if (timeoutHint > 0 && timeoutHint <= MAXIMUM_TIMEOUT) { - long now = (System.currentTimeMillis() + 999) / 1000; // round up - this.timeoutTime = now + timeoutHint; + long now = System.currentTimeMillis(); + this.timeoutTime = now + timeoutHint * 1000; } else { this.timeoutTime = Long.MAX_VALUE; } @@ -287,9 +287,9 @@ } buffer.append("holder:"); if (lockHolder != null) { - buffer.append(lockHolder.getUserID()); + buffer.append(lockHolder.getUserID()).append(' '); } else { - buffer.append("none"); + buffer.append("none "); } buffer.append("owner:").append(lockOwner); buffer.append(')'); Index: src/test/java/org/apache/jackrabbit/core/lock/LockTimeoutTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/lock/LockTimeoutTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/core/lock/LockTimeoutTest.java (revision 0) @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.Node; +import javax.jcr.Session; +import javax.jcr.lock.Lock; +import javax.transaction.UserTransaction; +import org.apache.jackrabbit.core.UserTransactionImpl; +import org.apache.jackrabbit.test.AbstractJCRTest; + +/** + * Test lock timeout expiration. + */ +public class LockTimeoutTest extends AbstractJCRTest { + + public void testExpired() throws Exception { + testExpired(false); + testExpired(true); + } + + private void testExpired(boolean xa) throws Exception { + Session s = testRootNode.getSession(); + Node n = testRootNode.addNode(nodeName1); + n.addMixin(mixLockable); + s.save(); + + UserTransaction utx = null; + if (xa) { + utx = new UserTransactionImpl(s); + utx.begin(); + } + + javax.jcr.lock.LockManager lm = s.getWorkspace().getLockManager(); + + boolean isDeep; + boolean isSessionScoped; + long timeoutHint; + String ownerInfo; + + isDeep = false; + isSessionScoped = false; + timeoutHint = 1; + ownerInfo = ""; + Session s2 = getHelper().getSuperuserSession(); + + Lock l = lm.lock(n.getPath(), isDeep, isSessionScoped, timeoutHint, ownerInfo); + // this works only for timeout = 1, + // as getSecondsRemaining always returns a positive value + assertEquals(timeoutHint, l.getSecondsRemaining()); + assertTrue(l.isLive()); + + if (xa) { + utx.commit(); + } + + long start = System.currentTimeMillis(); + while (true) { + Thread.sleep(100); + long now = System.currentTimeMillis(); + boolean success; + try { + s2.getNode(n.getPath()).setProperty("x", 1); + s2.save(); + success = true; + } catch (Exception e) { + success = false; + } + long t = now - start; + if (t > timeoutHint + 3000) { + assertTrue(success); + break; + } else if (t < timeoutHint) { + assertFalse(success); + } + } + n.remove(); + s.save(); + } + +} Index: src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java (revision 1033877) +++ src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java (working copy) @@ -139,8 +139,9 @@ return Long.MAX_VALUE; } - long now = (System.currentTimeMillis() + 999) / 1000; // round up - return Math.max(timeout - now, 1); // must always be positive + long remainingSeconds = (timeout - System.currentTimeMillis()) / 1000; + + return Math.max(remainingSeconds, 1); // must always be positive } /** Index: src/main/java/org/apache/jackrabbit/core/lock/XAEnvironment.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/lock/XAEnvironment.java (revision 1033877) +++ src/main/java/org/apache/jackrabbit/core/lock/XAEnvironment.java (working copy) @@ -149,7 +149,7 @@ // create a new lock info for this node String lockOwner = (ownerInfo != null) ? ownerInfo : node.getSession().getUserID(); - info = new XALockInfo(node, isSessionScoped, isDeep, lockOwner); + info = new XALockInfo(node, isSessionScoped, isDeep, timeoutHint, lockOwner); SessionImpl session = (SessionImpl) node.getSession(); info.setLockHolder(session); info.setLive(true); @@ -444,8 +444,8 @@ */ public XALockInfo( NodeImpl node, - boolean sessionScoped, boolean deep, String lockOwner) { - super(node.getNodeId(), sessionScoped, deep, lockOwner, Long.MAX_VALUE); + boolean sessionScoped, boolean deep, long timeoutHint, String lockOwner) { + super(node.getNodeId(), sessionScoped, deep, lockOwner, timeoutHint); this.node = node; } @@ -481,7 +481,9 @@ } else { LockInfo internalLock = lockMgr.internalLock( node, isDeep(), isSessionScoped(), - getTimeoutTime(), getLockOwner()); + getTimeoutHint(), getLockOwner()); LockInfo xaEnvLock = getLockInfo(node); // Check if the lockToken has been removed in the transaction ... if (xaEnvLock != null && xaEnvLock.getLockHolder() == null) {