You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2007/05/25 15:49:16 UTC
svn commit: r541651 - in /jackrabbit/trunk/jackrabbit-core/src:
main/java/org/apache/jackrabbit/core/
main/java/org/apache/jackrabbit/core/util/
test/java/org/apache/jackrabbit/core/util/
Author: stefan
Date: Fri May 25 06:49:15 2007
New Revision: 541651
URL: http://svn.apache.org/viewvc?view=rev&rev=541651
Log:
JCR-933: RepositoryImpl.acquireRepositoryLock() fails to detect that the file lock is already held by the current process
committing jukka's patch
Added:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/RepositoryLock.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/RepositoryLockTest.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/TestAll.java
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.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?view=diff&rev=541651&r1=541650&r2=541651
==============================================================================
--- 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 Fri May 25 06:49:15 2007
@@ -55,6 +55,7 @@
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.state.ManagedMLRUItemStateCacheFactory;
import org.apache.jackrabbit.core.state.SharedItemStateManager;
+import org.apache.jackrabbit.core.util.RepositoryLock;
import org.apache.jackrabbit.core.version.VersionManager;
import org.apache.jackrabbit.core.version.VersionManagerImpl;
import org.apache.jackrabbit.name.NamespaceResolver;
@@ -69,10 +70,6 @@
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Arrays;
@@ -105,11 +102,6 @@
private static Logger log = LoggerFactory.getLogger(RepositoryImpl.class);
/**
- * repository home lock
- */
- private static final String REPOSITORY_LOCK = ".lock";
-
- /**
* hardcoded id of the repository root node
*/
public static final NodeId ROOT_NODE_ID = NodeId.valueOf("cafebabe-cafe-babe-cafe-babecafebabe");
@@ -193,7 +185,7 @@
/**
* the lock that guards instantiation of multiple repositories.
*/
- private FileLock repLock;
+ private RepositoryLock repLock;
/**
* Clustered node used, <code>null</code> if clustering is not configured.
@@ -232,7 +224,9 @@
this.repConfig = repConfig;
- acquireRepositoryLock();
+ // Acquire a lock on the repository home
+ repLock = new RepositoryLock(repConfig.getHomeDir());
+ repLock.acquire();
// setup file systems
repStore = repConfig.getFileSystemConfig().createFileSystem();
@@ -393,67 +387,6 @@
}
/**
- * Lock the repository home.
- *
- * @throws RepositoryException if the repository lock can not be acquired
- */
- protected void acquireRepositoryLock() throws RepositoryException {
- File home = new File(this.repConfig.getHomeDir());
- File lock = new File(home, REPOSITORY_LOCK);
-
- if (lock.exists()) {
- log.warn("Existing lock file at " + lock.getAbsolutePath()
- + " detected. Repository was not shut down properly.");
- } else {
- try {
- lock.createNewFile();
- } catch (IOException e) {
- throw new RepositoryException(
- "Unable to create lock file at " + lock.getAbsolutePath(), e);
- }
- }
- try {
- repLock = new RandomAccessFile(lock, "rw").getChannel().tryLock();
- } catch (IOException e) {
- throw new RepositoryException(
- "Unable to lock file at " + lock.getAbsolutePath(), e);
- } catch (OverlappingFileLockException e) {
- throw new RepositoryException(
- "The repository home at " + home.getAbsolutePath()
- + " appears to be in use since the file at "
- + lock.getAbsolutePath() + " is already locked by the current process.");
- }
- if (repLock == null) {
- throw new RepositoryException(
- "The repository home at " + home.getAbsolutePath()
- + " appears to be in use since the file at "
- + lock.getAbsolutePath() + " is locked by another process.");
- }
- }
-
- /**
- * Release repository lock
- */
- protected void releaseRepositoryLock() {
- if (repLock != null) {
- try {
- FileChannel channel = repLock.channel();
- repLock.release();
- channel.close();
- } catch (IOException e) {
- // ignore
- }
- }
- repLock = null;
-
- File home = new File(this.repConfig.getHomeDir());
- File lock = new File(home, REPOSITORY_LOCK);
- if (!lock.delete()) {
- log.error("Unable to release repository lock");
- }
- }
-
- /**
* Returns the root node uuid.
* @param fs
* @return
@@ -1041,7 +974,7 @@
notifyAll();
// finally release repository lock
- releaseRepositoryLock();
+ repLock.release();
log.info("Repository has been shutdown");
}
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/RepositoryLock.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/RepositoryLock.java?view=auto&rev=541651
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/RepositoryLock.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/util/RepositoryLock.java Fri May 25 06:49:15 2007
@@ -0,0 +1,188 @@
+/*
+ * 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.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+
+import javax.jcr.RepositoryException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Exclusive lock on a repository home directory. This class encapsulates
+ * collective experience on how to acquire an exclusive lock on a given
+ * directory. The lock is expected to be exclusive both across process
+ * boundaries and within a single JVM. The lock mechanism must also work
+ * consistently on a variety of operating systems and JVM implementations.
+ *
+ * @see https://issues.apache.org/jira/browse/JCR-213
+ * @see https://issues.apache.org/jira/browse/JCR-233
+ * @see https://issues.apache.org/jira/browse/JCR-254
+ * @see https://issues.apache.org/jira/browse/JCR-912
+ * @see https://issues.apache.org/jira/browse/JCR-933
+ */
+public class RepositoryLock {
+
+ /**
+ * Name of the lock file within a directory.
+ */
+ private static final String LOCK = ".lock";
+
+ /**
+ * Logger instance.
+ */
+ private static final Logger log =
+ LoggerFactory.getLogger(RepositoryLock.class);
+
+ /**
+ * The locked directory.
+ */
+ private final File directory;
+
+ /**
+ * The lock file within the given directory.
+ */
+ private final File file;
+
+ /**
+ * Unique identifier (canonical path name) of the locked directory.
+ * Used to ensure exclusive locking within a single JVM.
+ *
+ * @see https://issues.apache.org/jira/browse/JCR-933
+ */
+ private final String identifier;
+
+ /**
+ * The file lock. Used to ensure exlusive lockin across process boundaries.
+ *
+ * @see https://issues.apache.org/jira/browse/JCR-233
+ */
+ private FileLock lock;
+
+ /**
+ * Creates a lock instance for the given directory path. An instantiated
+ * lock still needs to be explicitly acquired using the {@link #acquire()}
+ * method.
+ *
+ * @param path directory path
+ * @throws RepositoryException if the canonical path of the directory
+ * can not be determined
+ */
+ public RepositoryLock(String path) throws RepositoryException {
+ try {
+ directory = new File(path).getCanonicalFile();
+ file = new File(directory, LOCK);
+ identifier = RepositoryLock.class.getName()
+ + ":" + directory.getPath().intern();
+ lock = null;
+ } catch (IOException e) {
+ throw new RepositoryException(
+ "Unable to determine canonical path of " + path, e);
+ }
+ }
+
+ /**
+ * Lock the repository home.
+ *
+ * @throws RepositoryException if the repository lock can not be acquired
+ */
+ public void acquire() throws RepositoryException {
+ if (file.exists()) {
+ log.warn("Existing lock file " + file + " detected."
+ + " Repository was not shut down properly.");
+ }
+
+ try {
+ lock = new RandomAccessFile(file, "rw").getChannel().tryLock();
+ } catch (IOException e) {
+ throw new RepositoryException(
+ "Unable to create or lock file " + file, e);
+ } catch (OverlappingFileLockException e) {
+ // JCR-912: OverlappingFileLockException with JRE 1.6
+ throw new RepositoryException(
+ "The repository home " + directory + " appears to be in use"
+ + " since the file named " + file.getName()
+ + " is already locked by the current process.");
+ }
+
+ if (lock == null) {
+ throw new RepositoryException(
+ "The repository home " + directory + " appears to be in use"
+ + " since the file named " + file.getName()
+ + " is locked by another process.");
+ }
+
+ // JCR-933: due to a bug in java 1.4/1.5 on *nix platforms
+ // it's possible that java.nio.channels.FileChannel.tryLock()
+ // returns a non-null FileLock object although the lock is already
+ // held by *this* jvm process
+ synchronized (identifier) {
+ if (null != System.getProperty(identifier)) {
+ // note that the newly acquired (redundant) file lock
+ // is deliberately *not* released because this could
+ // potentially cause, depending on the implementation,
+ // the previously acquired lock(s) to be released
+ // as well
+ throw new RepositoryException(
+ "The repository home " + directory + " appears to be"
+ + " already locked by the current process.");
+ } else {
+ try {
+ System.setProperty(identifier, identifier);
+ } catch (SecurityException e) {
+ log.warn("Unable to set system property: " + identifier, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Releases repository lock.
+ */
+ public void release() {
+ if (lock != null) {
+ try {
+ FileChannel channel = lock.channel();
+ lock.release();
+ channel.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ lock = null;
+ }
+
+ if (!file.delete()) {
+ log.error("Unable to release repository lock");
+ }
+
+ // JCR-933: see #acquire()
+ synchronized (identifier) {
+ try {
+ System.getProperties().remove(identifier);
+ } catch (SecurityException e) {
+ log.error("Unable to clear system property: " + identifier, e);
+ }
+ }
+ }
+
+}
Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/RepositoryLockTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/RepositoryLockTest.java?view=auto&rev=541651
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/RepositoryLockTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/RepositoryLockTest.java Fri May 25 06:49:15 2007
@@ -0,0 +1,125 @@
+/*
+ * 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.util;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.util.RepositoryLock;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the {@link RepositoryLock} class.
+ */
+public class RepositoryLockTest extends TestCase {
+
+ /**
+ * The temporary directory used for testing.
+ */
+ private File directory;
+
+ /**
+ * Sets up the temporary directory used for testing.
+ */
+ protected void setUp() throws IOException {
+ directory = File.createTempFile("RepositoryLock", "Test");
+ directory.delete();
+ directory.mkdir();
+ }
+
+ /**
+ * Deletes the temporary directory used for testing.
+ */
+ protected void tearDown() {
+ delete(directory);
+ }
+
+ /**
+ * Recursively deletes the given file or directory.
+ *
+ * @param file file or directory to be deleted
+ */
+ private void delete(File file) {
+ File[] files = file.listFiles();
+ for (int i = 0; files != null && i < files.length; i++) {
+ delete(files[i]);
+ }
+ file.delete();
+ }
+
+ /**
+ * Tests that when an acquired lock is released, the lock file is
+ * automatically removed.
+ *
+ * @throws RepositoryException if an error occurs
+ */
+ public void testNoFilesLeftBehind() throws RepositoryException {
+ RepositoryLock lock = new RepositoryLock(directory.getPath());
+ lock.acquire();
+ lock.release();
+ assertEquals(
+ "Some files left behind by a lock",
+ 0, directory.listFiles().length);
+ }
+
+ /**
+ * Tests that locking is exclusive within a single JVM.
+ *
+ * @throws RepositoryException if an error occurs
+ */
+ public void testTwoLocks() throws RepositoryException {
+ RepositoryLock lockA = new RepositoryLock(directory.getPath());
+ RepositoryLock lockB = new RepositoryLock(directory.getPath());
+ lockA.acquire();
+ try {
+ lockB.acquire();
+ fail("Can acquire an already acquired lock");
+ } catch (RepositoryException e) {
+ }
+ lockA.release();
+ try {
+ lockB.acquire();
+ } catch (RepositoryException e) {
+ fail("Can not acquire a released lock");
+ }
+ lockB.release();
+ }
+
+ /**
+ * Tests that the canonical path is used for locking.
+ *
+ * @see https://issues.apache.org/jira/browse/JCR-933
+ * @throws RepositoryException
+ */
+ public void testCanonicalPath() throws RepositoryException {
+ RepositoryLock lockA = new RepositoryLock(directory.getPath());
+ lockA.acquire();
+ try {
+ File parent = new File(directory, "..");
+ RepositoryLock lockB = new RepositoryLock(
+ new File(parent, directory.getName()).getPath());
+ lockB.acquire();
+ fail("Can acquire an already acquired lock using a different path");
+ } catch (RepositoryException e) {
+ }
+ lockA.release();
+ }
+
+}
Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/TestAll.java?view=auto&rev=541651
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/util/TestAll.java Fri May 25 06:49:15 2007
@@ -0,0 +1,38 @@
+/*
+ * 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.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all testcases for the util module.
+ */
+public class TestAll extends TestCase {
+
+ /**
+ * Returns a test suite that executes all tests inside this package.
+ *
+ * @return a test suite that executes all tests inside this package
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Utility tests");
+ suite.addTestSuite(RepositoryLockTest.class);
+ return suite;
+ }
+}