com.qcloud.cos.internal.FileLocks Maven / Gradle / Ivy
Show all versions of cos_api Show documentation
package com.qcloud.cos.internal;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Map;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.qcloud.cos.exception.FileLockException;
import com.qcloud.cos.utils.IOUtils;
/**
* An internal utility used to provide both inter and intra JVM file locking.
* This works well as long as this class is loaded with the same class loader in
* a JVM. Otherwise, intra-JVM file locking is not guaranteed.
*
* Per javadoc of {@link FileLock}, "File locks are held on behalf of the entire
* Java virtual machine. They are not suitable for controlling access to a file
* by multiple threads within the same virtual machine."
*
* Hence the need for this utility class.
*/
public enum FileLocks {
;
// External file lock doesn't seem to work correctly on Windows,
// so disabling for now (Ref: TT0047889941)
private static final boolean EXTERNAL_LOCK = false;
private static final Logger log = LoggerFactory.getLogger(FileLocks.class);
private static final Map lockedFiles = new TreeMap();
/**
* Acquires an exclusive lock on the specified file, creating the file as
* necessary. Caller of this method is responsible to call the
* {@link #unlock(File)} method to prevent release leakage.
*
* @return true if the locking is successful; false otherwise.
*
* @throws FileLockException if we failed to lock the file
*/
public static boolean lock(File file) {
synchronized(lockedFiles) {
if (lockedFiles.containsKey(file))
return false; // already locked
}
FileLock lock = null;
RandomAccessFile raf = null;
try {
// Note if the file does not already exist then an attempt will be
// made to create it because of the use of "rw".
raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();
if (EXTERNAL_LOCK)
lock = channel.lock();
} catch (Exception e) {
IOUtils.closeQuietly(raf, log);
throw new FileLockException(e);
}
final boolean locked;
synchronized(lockedFiles) {
RandomAccessFile prev = lockedFiles.put(file, raf);
if (prev == null) {
locked = true;
} else {
// race condition: some other thread got locked it before this
locked = false;
lockedFiles.put(file, prev); // put it back
}
}
if (locked) {
if (log.isDebugEnabled())
log.debug("Locked file " + file + " with " + lock);
} else {
IOUtils.closeQuietly(raf, log);
}
return locked;
}
/**
* Returns true if the specified file is currently locked; false otherwise.
*/
public static boolean isFileLocked(File file) {
synchronized(lockedFiles) {
return lockedFiles.containsKey(file);
}
}
/**
* Unlocks a file previously locked via {@link #lock(File)}.
*
* @return true if the unlock is successful; false otherwise. Successful
* unlock means we have found and attempted to close the locking
* file channel, but ignoring the fact that the close operation may
* have actually failed.
*/
public static boolean unlock(File file) {
synchronized(lockedFiles) {
final RandomAccessFile raf = lockedFiles.get(file);
if (raf == null)
return false;
else {
// Must close out the channel before removing it from the map;
// or else risk giving a false negative (of no lock but in fact
// the file is still locked by the file system.)
IOUtils.closeQuietly(raf, log);
lockedFiles.remove(file);
}
}
if (log.isDebugEnabled())
log.debug("Unlocked file " + file);
return true;
}
}