com.graphhopper.storage.NativeFSLockFactory Maven / Gradle / Ivy
Show all versions of graphhopper-core Show documentation
/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH 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 com.graphhopper.storage;
import com.graphhopper.util.Helper;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
/**
* Creates a write lock file. Influenced by Lucene code
*
*
* @author Peter Karich
*/
public class NativeFSLockFactory implements LockFactory {
private File lockDir;
public NativeFSLockFactory() {
}
public NativeFSLockFactory(File dir) {
this.lockDir = dir;
}
public static void main(String[] args) throws IOException {
// trying FileLock mechanics in different processes
File file = new File("tmp.lock");
file.createNewFile();
FileChannel channel = new RandomAccessFile(file, "r").getChannel();
boolean shared = true;
FileLock lock1 = channel.tryLock(0, Long.MAX_VALUE, shared);
System.out.println("locked " + lock1);
System.in.read();
System.out.println("release " + lock1);
lock1.release();
}
@Override
public void setLockDir(File lockDir) {
this.lockDir = lockDir;
}
@Override
public synchronized GHLock create(String fileName, boolean writeAccess) {
if (lockDir == null)
throw new RuntimeException("Set lockDir before creating " + (writeAccess ? "write" : "read") + " locks");
return new NativeLock(lockDir, fileName, writeAccess);
}
@Override
public synchronized void forceRemove(String fileName, boolean writeAccess) {
if (lockDir.exists()) {
create(fileName, writeAccess).release();
File lockFile = new File(lockDir, fileName);
if (lockFile.exists() && !lockFile.delete())
throw new RuntimeException("Cannot delete " + lockFile);
}
}
static class NativeLock implements GHLock {
private final String name;
private final File lockDir;
private final File lockFile;
private final boolean writeLock;
private RandomAccessFile tmpRaFile;
private FileChannel tmpChannel;
private FileLock tmpLock;
private Exception failedReason;
public NativeLock(File lockDir, String fileName, boolean writeLock) {
this.name = fileName;
this.lockDir = lockDir;
this.lockFile = new File(lockDir, fileName);
this.writeLock = writeLock;
}
@Override
public synchronized boolean tryLock() {
// already locked
if (lockExists())
return false;
// on-the-fly: make sure directory exists
if (!lockDir.exists()) {
if (!lockDir.mkdirs())
throw new RuntimeException("Directory " + lockDir + " does not exist and cannot be created to place lock file there: " + lockFile);
}
if (!lockDir.isDirectory())
throw new IllegalArgumentException("lockDir has to be a directory: " + lockDir);
try {
failedReason = null;
// we need write access even for read locks - in order to create the lock file!
tmpRaFile = new RandomAccessFile(lockFile, "rw");
} catch (IOException ex) {
failedReason = ex;
return false;
}
try {
tmpChannel = tmpRaFile.getChannel();
try {
tmpLock = tmpChannel.tryLock(0, Long.MAX_VALUE, !writeLock);
// OverlappingFileLockException is not an IOException!
} catch (Exception ex) {
failedReason = ex;
} finally {
if (tmpLock == null) {
Helper.close(tmpChannel);
tmpChannel = null;
}
}
} finally {
if (tmpChannel == null) {
Helper.close(tmpRaFile);
tmpRaFile = null;
}
}
return lockExists();
}
private synchronized boolean lockExists() {
return tmpLock != null;
}
@Override
public synchronized boolean isLocked() {
if (!lockFile.exists())
return false;
if (lockExists())
return true;
try {
boolean obtained = tryLock();
if (obtained)
release();
return !obtained;
} catch (Exception ex) {
return false;
}
}
@Override
public synchronized void release() {
if (lockExists()) {
try {
failedReason = null;
tmpLock.release();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
tmpLock = null;
try {
tmpChannel.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
tmpChannel = null;
try {
tmpRaFile.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
tmpRaFile = null;
}
}
}
lockFile.delete();
}
}
@Override
public String getName() {
return name;
}
@Override
public Exception getObtainFailedReason() {
return failedReason;
}
@Override
public String toString() {
return lockFile.toString();
}
}
}