package org.apache.lucene.store; /** * 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. */ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.io.File; import java.io.RandomAccessFile; import java.io.IOException; import java.util.HashSet; import java.util.Random; /** *
Implements {@link LockFactory} using native OS file * locks. Note that because this LockFactory relies on * java.nio.* APIs for locking, any problems with those APIs * will cause locking to fail. Specifically, on certain NFS * environments the java.nio.* locks will fail (the lock can * incorrectly be double acquired) whereas {@link * SimpleFSLockFactory} worked perfectly in those same * environments. For NFS based access to an index, it's * recommended that you try {@link SimpleFSLockFactory} * first and work around the one limitation that a lock file * could be left when the JVM exits abnormally.
* *The primary benefit of {@link NativeFSLockFactory} is * that lock files will be properly removed (by the OS) if * the JVM has an abnormal exit.
* *Note that, unlike {@link SimpleFSLockFactory}, the existence of * leftover lock files in the filesystem on exiting the JVM * is fine because the OS will free the locks held against * these files even though the files still remain.
* *If you suspect that this or any other LockFactory is * not working properly in your environment, you can easily * test it by using {@link VerifyingLockFactory}, {@link * LockVerifyServer} and {@link LockStressTest}.
* * @see LockFactory */ public class NativeFSLockFactory extends FSLockFactory { private volatile boolean tested = false; // Simple test to verify locking system is "working". On // NFS, if it's misconfigured, you can hit long (35 // second) timeouts which cause Lock.obtain to take far // too long (it assumes the obtain() call takes zero // time). private synchronized void acquireTestLock() { if (tested) return; tested = true; // Ensure that lockDir exists and is a directory. if (!lockDir.exists()) { if (!lockDir.mkdirs()) throw new RuntimeException("Cannot create directory: " + lockDir.getAbsolutePath()); } else if (!lockDir.isDirectory()) { throw new RuntimeException("Found regular file where directory expected: " + lockDir.getAbsolutePath()); } String randomLockName = "lucene-" + Long.toString(new Random().nextInt(), Character.MAX_RADIX) + "-test.lock"; Lock l = makeLock(randomLockName); try { l.obtain(); l.release(); } catch (IOException e) { RuntimeException e2 = new RuntimeException("Failed to acquire random test lock; please verify filesystem for lock directory '" + lockDir + "' supports locking"); e2.initCause(e); throw e2; } } /** * Create a NativeFSLockFactory instance, with null (unset) * lock directory. When you pass this factory to a {@link FSDirectory} * subclass, the lock directory is automatically set to the * directory itsself. Be sure to create one instance for each directory * your create! */ public NativeFSLockFactory() throws IOException { this((File) null); } /** * Create a NativeFSLockFactory instance, storing lock * files into the specified lockDirName: * * @param lockDirName where lock files are created. */ public NativeFSLockFactory(String lockDirName) throws IOException { this(new File(lockDirName)); } /** * Create a NativeFSLockFactory instance, storing lock * files into the specified lockDir: * * @param lockDir where lock files are created. */ public NativeFSLockFactory(File lockDir) throws IOException { setLockDir(lockDir); } @Override public synchronized Lock makeLock(String lockName) { acquireTestLock(); if (lockPrefix != null) lockName = lockPrefix + "-" + lockName; return new NativeFSLock(lockDir, lockName); } @Override public void clearLock(String lockName) throws IOException { // Note that this isn't strictly required anymore // because the existence of these files does not mean // they are locked, but, still do this in case people // really want to see the files go away: if (lockDir.exists()) { if (lockPrefix != null) { lockName = lockPrefix + "-" + lockName; } File lockFile = new File(lockDir, lockName); if (lockFile.exists() && !lockFile.delete()) { throw new IOException("Cannot delete " + lockFile); } } } }; class NativeFSLock extends Lock { private RandomAccessFile f; private FileChannel channel; private FileLock lock; private File path; private File lockDir; /* * The javadocs for FileChannel state that you should have * a single instance of a FileChannel (per JVM) for all * locking against a given file. To ensure this, we have * a single (static) HashSet that contains the file paths * of all currently locked locks. This protects against * possible cases where different Directory instances in * one JVM (each with their own NativeFSLockFactory * instance) have set the same lock dir and lock prefix. */ private static HashSet