/*
 * 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.lucene.store;


import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;

Implements LockFactory using Files.createFile.

The main downside with using this API for locking is that the Lucene write lock may not be released when the JVM exits abnormally.

When this happens, an LockObtainFailedException is hit when trying to create a writer, in which case you may need to explicitly clear the lock file first by manually removing the file. But, first be certain that no writer is in fact writing to the index otherwise you can easily corrupt your index.

Special care needs to be taken if you change the locking implementation: First be certain that no writer is in fact writing to the index otherwise you can easily corrupt your index. Be sure to do the LockFactory change all Lucene instances and clean up all leftover lock files before starting the new configuration for the first time. Different implementations can not work together!

If you suspect that this or any other LockFactory is not working properly in your environment, you can easily test it by using VerifyingLockFactory, LockVerifyServer and LockStressTest.

This is a singleton, you have to use INSTANCE.

See Also:
/** * <p>Implements {@link LockFactory} using {@link * Files#createFile}.</p> * * <p>The main downside with using this API for locking is * that the Lucene write lock may not be released when * the JVM exits abnormally.</p> * * <p>When this happens, an {@link LockObtainFailedException} * is hit when trying to create a writer, in which case you may * need to explicitly clear the lock file first by * manually removing the file. But, first be certain that * no writer is in fact writing to the index otherwise you * can easily corrupt your index.</p> * * <p>Special care needs to be taken if you change the locking * implementation: First be certain that no writer is in fact * writing to the index otherwise you can easily corrupt * your index. Be sure to do the LockFactory change all Lucene * instances and clean up all leftover lock files before starting * the new configuration for the first time. Different implementations * can not work together!</p> * * <p>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}.</p> * * <p>This is a singleton, you have to use {@link #INSTANCE}. * * @see LockFactory */
public final class SimpleFSLockFactory extends FSLockFactory {
Singleton instance
/** * Singleton instance */
public static final SimpleFSLockFactory INSTANCE = new SimpleFSLockFactory(); private SimpleFSLockFactory() {} @Override protected Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException { Path lockDir = dir.getDirectory(); // Ensure that lockDir exists and is a directory. // note: this will fail if lockDir is a symlink Files.createDirectories(lockDir); Path lockFile = lockDir.resolve(lockName); // create the file: this will fail if it already exists try { Files.createFile(lockFile); } catch (FileAlreadyExistsException | AccessDeniedException e) { // convert optional specific exception to our optional specific exception throw new LockObtainFailedException("Lock held elsewhere: " + lockFile, e); } // used as a best-effort check, to see if the underlying file has changed final FileTime creationTime = Files.readAttributes(lockFile, BasicFileAttributes.class).creationTime(); return new SimpleFSLock(lockFile, creationTime); } static final class SimpleFSLock extends Lock { private final Path path; private final FileTime creationTime; private volatile boolean closed; SimpleFSLock(Path path, FileTime creationTime) throws IOException { this.path = path; this.creationTime = creationTime; } @Override public void ensureValid() throws IOException { if (closed) { throw new AlreadyClosedException("Lock instance already released: " + this); } // try to validate the backing file name, that it still exists, // and has the same creation time as when we obtained the lock. // if it differs, someone deleted our lock file (and we are ineffective) FileTime ctime = Files.readAttributes(path, BasicFileAttributes.class).creationTime(); if (!creationTime.equals(ctime)) { throw new AlreadyClosedException("Underlying file changed by an external force at " + ctime + ", (lock=" + this + ")"); } } @Override public synchronized void close() throws IOException { if (closed) { return; } try { // NOTE: unlike NativeFSLockFactory, we can potentially delete someone else's // lock if things have gone wrong. we do best-effort check (ensureValid) to // avoid doing this. try { ensureValid(); } catch (Throwable exc) { // notify the user they may need to intervene. throw new LockReleaseFailedException("Lock file cannot be safely removed. Manual intervention is recommended.", exc); } // we did a best effort check, now try to remove the file. if something goes wrong, // we need to make it clear to the user that the directory may still remain locked. try { Files.delete(path); } catch (Throwable exc) { throw new LockReleaseFailedException("Unable to remove lock file. Manual intervention is recommended", exc); } } finally { closed = true; } } @Override public String toString() { return "SimpleFSLock(path=" + path + ",creationTime=" + creationTime + ")"; } } }