org.apache.lucene.store.SimpleFSLockFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.store.SimpleFSLockFactory.java

Source

/*
 * 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;

/**
 * <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
     */
    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 + ")";
        }
    }
}