You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2009/08/18 16:51:34 UTC
svn commit: r805431 - in /jackrabbit/trunk:
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/
jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/
jackrabbit-jcr2...
Author: jukka
Date: Tue Aug 18 14:51:34 2009
New Revision: 805431
URL: http://svn.apache.org/viewvc?rev=805431&view=rev
Log:
JCR-1590: JSR 283: Locking
Added a timeout handler to LockManagerImpl. It runs as a scheduled task (at one second intervals) inside a new repository-wide ScheduledExecutorService instance.
Adjusted the TCK test for this and fixed some jcr2spi assumptions about this (the lock can expire from under the SPI layer).
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/AbstractLockTest.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Tue Aug 18 14:51:34 2009
@@ -34,6 +34,12 @@
import java.util.Properties;
import java.util.Set;
import java.util.Iterator;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
@@ -252,6 +258,11 @@
private WorkspaceEventChannel createWorkspaceEventChannel;
/**
+ * Scheduled executor service.
+ */
+ private final ScheduledExecutorService executor;
+
+ /**
* Protected constructor.
*
* @param repConfig the repository configuration.
@@ -260,6 +271,10 @@
* or another error occurs.
*/
protected RepositoryImpl(RepositoryConfig repConfig) throws RepositoryException {
+ ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
+ Runtime.getRuntime().availableProcessors() * 2,
+ new ThreadPoolExecutor.CallerRunsPolicy());
+ this.executor = executor;
// Acquire a lock on the repository home
repLock = repConfig.getRepositoryLockMechanism();
@@ -1166,6 +1181,18 @@
// wake up threads waiting on this instance's monitor (e.g. workspace janitor)
notifyAll();
+ // Shut down the executor service
+ executor.shutdown();
+ try {
+ // Wait for all remaining background threads to terminate
+ if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
+ log.warn("Attempting to forcibly shutdown runaway threads");
+ executor.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ log.warn("Interrupted while waiting for background threads", e);
+ }
+
// finally release repository lock
if (repLock != null) {
try {
@@ -1841,7 +1868,8 @@
// lock manager is lazily instantiated in order to avoid
// 'chicken & egg' bootstrap problems
if (lockMgr == null) {
- lockMgr = new LockManagerImpl(getSystemSession(), fs);
+ lockMgr =
+ new LockManagerImpl(getSystemSession(), fs, executor);
if (clusterNode != null && config.isClustered()) {
lockChannel = clusterNode.createLockChannel(getName());
lockMgr.setEventChannel(lockChannel);
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java Tue Aug 18 14:51:34 2009
@@ -131,18 +131,16 @@
public long getSecondsRemaining() {
if (!info.isLive()) {
return -1;
- } else {
- return Long.MAX_VALUE;
}
// TODO JCR-1590: Disabled until locks get unlocked when they timeout
-// long timeout = info.getTimeoutTime();
-// if (timeout == Long.MAX_VALUE) {
-// return Long.MAX_VALUE;
-// }
-//
-// long now = (System.currentTimeMillis() + 999) / 1000; // round up
-// return Math.max(timeout - now, 1); // must always be positive
+ long timeout = info.getTimeoutTime();
+ if (timeout == Long.MAX_VALUE) {
+ return Long.MAX_VALUE;
+ }
+
+ long now = (System.currentTimeMillis() + 999) / 1000; // round up
+ return Math.max(timeout - now, 1); // must always be positive
}
/**
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java Tue Aug 18 14:51:34 2009
@@ -227,6 +227,11 @@
return timeoutTime;
}
+ public boolean isExpired() {
+ return timeoutTime != Long.MAX_VALUE
+ && timeoutTime * 1000 > System.currentTimeMillis();
+ }
+
/**
* Updates the timeout time of this lock based on current time and
* the timeout hint specified for this lock. The timeout time is always
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java Tue Aug 18 14:51:34 2009
@@ -69,6 +69,9 @@
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
/**
* Provides the functionality needed for locking and unlocking nodes.
@@ -134,6 +137,11 @@
};
/**
+ * The periodically invoked lock timeout handler.
+ */
+ private final ScheduledFuture<?> timeoutHandler;
+
+ /**
* System session
*/
private final SessionImpl sysSession;
@@ -156,12 +164,14 @@
/**
* Create a new instance of this class.
*
- * @param session system session
- * @param fs file system for persisting locks
+ * @param session system session
+ * @param fs file system for persisting locks
+ * @param executor scheduled executor service for handling lock timeouts
* @throws RepositoryException if an error occurs
*/
- public LockManagerImpl(SessionImpl session, FileSystem fs)
- throws RepositoryException {
+ public LockManagerImpl(
+ SessionImpl session, FileSystem fs,
+ ScheduledExecutorService executor) throws RepositoryException {
this.sysSession = session;
this.locksFile = new FileSystemResource(fs, FileSystem.SEPARATOR + LOCKS_FILE);
@@ -178,16 +188,51 @@
throw new RepositoryException("I/O error while reading locks from '"
+ locksFile.getPath() + "'", e);
}
+
+ timeoutHandler = executor.scheduleWithFixedDelay(
+ new TimeoutHandler(), 1, 1, TimeUnit.SECONDS);
}
/**
* Close this lock manager. Writes back all changes.
*/
public void close() {
+ timeoutHandler.cancel(false);
save();
}
/**
+ * Periodically (at one second delay) invoked timeout handler. Traverses
+ * all locks and unlocks those that have expired.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/JCR-1590">JCR-1590</a>:
+ * JSR 283: Locking
+ */
+ private class TimeoutHandler implements Runnable {
+ public void run() {
+ lockMap.traverse(new PathMap.ElementVisitor<LockInfo>() {
+ public void elementVisited(PathMap.Element<LockInfo> element) {
+ LockInfo info = element.get();
+ if (info != null && info.isLive() && info.isExpired()) {
+ NodeId id = info.getId();
+ SessionImpl holder = info.getLockHolder();
+ if (holder == null) {
+ info.setLockHolder(sysSession);
+ holder = sysSession;
+ }
+ try {
+ // FIXME: This session access is not thread-safe!
+ unlock(holder.getNodeById(id));
+ } catch (RepositoryException e) {
+ log.warn("Unable to expire the lock " + id, e);
+ }
+ }
+ }
+ }, false);
+ }
+ }
+
+ /**
* Read locks from locks file and populate path map
*/
private void load() throws FileSystemException {
Modified: jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/AbstractLockTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/AbstractLockTest.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/AbstractLockTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/lock/AbstractLockTest.java Tue Aug 18 14:51:34 2009
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.test.api.lock;
import javax.jcr.Node;
+import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
@@ -257,39 +258,33 @@
/**
* Test expiration of the lock
*/
- public void testLockExpiration() throws RepositoryException, NotExecutableException {
+ public synchronized void testLockExpiration()
+ throws RepositoryException, NotExecutableException {
lockedNode.unlock();
- ObservationManager obsMgr = superuser.getWorkspace().getObservationManager();
- EventResult listener = new EventResult(((JUnitTest) this).log);
- try {
- obsMgr.addEventListener(listener, Event.PROPERTY_REMOVED, lockedNode.getPath(), false, new String[0], new String[0], false);
-
- boolean lockPropRemoved = false;
- long hint = 1;
- lock = lockMgr.lock(lockedNode.getPath(), isDeep(), isSessionScoped(), hint, null);
- // only test if timeout hint was respected.
- if (lock.getSecondsRemaining() <= 1) {
- Event[] evts = listener.getEvents(2000);
- for (int i = 0; i < evts.length; i++) {
- if (evts[i].getType() == Event.PROPERTY_REMOVED &&
- evts[i].getPath().endsWith(jcrLockOwner)) {
- lockPropRemoved = true;
- // lock property has been removed -> make sure lock has
- // been released and lock.getSecondsRemaining behaves properly.
- assertTrue("A released lock must return a negative number of seconds", lock.getSecondsRemaining() < 0);
- assertFalse("If the timeout hint is respected the lock must be automatically released.", lock.isLive());
- assertFalse("If the timeout hint is respected the lock must be automatically released.", lockedNode.isLocked());
- }
- }
- if (!lockPropRemoved) {
- fail("If the timeout hint is respected the lock must be automatically released.");
- }
- } else {
- throw new NotExecutableException("timeout hint was ignored.");
+ long hint = 1;
+ lock = lockMgr.lock(
+ lockedNode.getPath(), isDeep(), isSessionScoped(), hint, null);
+
+ // only test if timeout hint was respected.
+ long remaining = lock.getSecondsRemaining();
+ if (remaining <= hint) {
+ try {
+ wait(remaining * 2000); // wait twice as long to be safe
+ } catch (InterruptedException ignore) {
}
- } finally {
- obsMgr.removeEventListener(listener);
+ assertTrue(
+ "A released lock must return a negative number of seconds",
+ lock.getSecondsRemaining() < 0);
+ String message = "If the timeout hint is respected the lock"
+ + " must be automatically released.";
+ assertFalse(message, lock.isLive());
+ assertFalse(message, lockedNode.isLocked());
+ assertFalse(message, lockMgr.isLocked(lockedNode.getPath()));
+ assertFalse(message, lockedNode.hasProperty(Property.JCR_LOCK_IS_DEEP));
+ assertFalse(message, lockedNode.hasProperty(Property.JCR_LOCK_OWNER));
+ } else {
+ throw new NotExecutableException("timeout hint was ignored.");
}
}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java?rev=805431&r1=805430&r2=805431&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java Tue Aug 18 14:51:34 2009
@@ -496,7 +496,7 @@
if (lockHoldingState == nodeState) {
return true;
} else {
- return lockInfo.isDeep();
+ return lockInfo != null && lockInfo.isDeep();
}
}
}
@@ -691,14 +691,20 @@
* @see Lock#getLockOwner()
*/
public String getLockOwner() {
- return getLockInfo().getOwner();
+ LockInfo info = getLockInfo();
+ if (info != null) {
+ return info.getOwner();
+ } else {
+ return null;
+ }
}
/**
* @see Lock#isDeep()
*/
public boolean isDeep() {
- return getLockInfo().isDeep();
+ LockInfo info = getLockInfo();
+ return info != null && info.isDeep();
}
/**
@@ -719,7 +725,12 @@
}
updateLockInfo();
- return getLockInfo().getLockToken();
+ LockInfo info = getLockInfo();
+ if (info != null) {
+ return info.getLockToken();
+ } else {
+ return null;
+ }
}
/**
@@ -734,7 +745,8 @@
* @see Lock#isSessionScoped()
*/
public boolean isSessionScoped() {
- return getLockInfo().isSessionScoped();
+ LockInfo info = getLockInfo();
+ return info != null && info.isSessionScoped();
}
/**
@@ -766,7 +778,8 @@
* @see javax.jcr.lock.Lock#isLockOwningSession()
*/
public boolean isLockOwningSession(){
- return lockState.lockInfo.isLockOwner();
+ LockInfo info = getLockInfo();
+ return info != null && info.isLockOwner();
}
//----------------------------------------------< LockTokenListener >---