Unable to lock files on Linux using java.nio.channels.FileLock - java

I was creating an application in Java for which I want only one instance running. For this purpose I created a file and got a lock while my application is running.
I have following code which works on Windows, but failed on Linux: once I acquire a lock without unlocking it I can get another lock on it.
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class MyApp {
private static File f;
private static FileChannel channel;
private static FileLock lock;
public static void main(String[] args) {
try {
f = new File("RingOnRequest.lock");
// Check if the lock exist
if (f.exists()) {
// if exist try to delete it
f.delete();
}
// Try to get the lock
channel = new RandomAccessFile(f, "rw").getChannel();
lock = channel.tryLock();
if(lock == null)
{
// File is lock by other application
channel.close();
throw new RuntimeException("Only 1 instance of MyApp can run.");
}
// Add shutdown hook to release lock when application shutdown
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
//Your application tasks here..
System.out.println("Running");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
catch(IOException e)
{
throw new RuntimeException("Could not start process.", e);
}
}
public static void unlockFile() {
// release and delete file lock
try {
if(lock != null) {
lock.release();
channel.close();
f.delete();
}
} catch(IOException e) {
e.printStackTrace();
}
}
static class ShutdownHook extends Thread {
public void run() {
unlockFile();
}
}
}

You are deleting the lock file every time you run, so only one process can have a lock on it.
When you use FileLock, it is purely advisory—acquiring a lock on a file may not stop you from doing anything…reading, writing, and deleting a file may all be possible even when another process has acquired a lock. Sometimes, a lock might do more than this on a particular platform, but this behavior is unspecified, and relying on more than is guaranteed in the class documentation is a recipe for failure.
An "advisory lock" is only a signal that is visible to other processes that bother to look for it. If you depend on it for more than that, your program will break when run on some other platform.
Why would you delete the lock file anyway? The lock file is just like a boolean flag that is visible to every process on the system. Design your protocol to use it that way, and you'll have a reliable, cross platform locking mechanism.

Why don't you save the PID into a file, and instead of locking the file, verify if there's a process with that ID. If there is, and it's an instance of your application, you know it's already running.
A socket might be a good idea as well, since you can use it to communicate to the running instance something.
EDIT:
Also, from FileLock's javadoc:
Whether or not a lock actually prevents another program from accessing
the content of the locked region is system-dependent and therefore
unspecified.

Use mkdir. On unix systems this is an atomic operation – it will succeed if a new directory is successfully created, otherwise it will fail.
Example:
File lockFile = new File("/path/to/lockdir");
boolean hasLock = lockFile.mkdir();
if (!hasLock) {
throw new IOException("could not get lock");
}
// do stuff
lockFile.delete();

I used same sample as you and got same problem on Mac OS X. It seems that file lock does not prevent file deletion on POSIX systems . Your app wil still have some kind of handle to that file until you unlock it. So consider using lock file with PID in it's name( or inside file).

I tested it on both Windows and Linux. Works fine. The lock file gets deleted automatically when the application closes normally. So you don't have to worry about the lock file staying there when you restart the application. Just comment out the following lines:
if (f.exists()) {
// if exist try to delete it
f.delete();
}
However, you may want to consider what happens if your application crashes and does not close in a normal fashion.

Recently i encountered the same kind of problem, but in my case i had an advantage: my application polled some directory only after some timeout. As my application did not immediately poll for directory i wrote special class that creates lock file with his own PID inside in init method, after that before it tries to work with directory it needs to call ownedLock() - if it returns true then we can work otherwise exit(code is in Kotlin but you will get the main idea):
import java.io.File
import java.lang.management.ManagementFactory
class DirectoryLocker(private val directory: String, private val lockName: String) {
private val lockFile by lazy { File("$directory/$lockName.lock") }
// Will try to acquire lock to directory, whoever last writes its pid to file owns the directory
fun acquireLock() = with(lockFile) {
createNewFile()
writeText(currentPID())
}
fun ownedLock(): Boolean = lockFilePid() == currentPID()
fun releaseOwnedLock() {
if(lockFilePid() == currentPID()) lockFile.delete()
}
private fun currentPID(): String {
val processName = ManagementFactory.getRuntimeMXBean().name
return processName.split("#".toRegex()).first()
}
private fun lockFilePid(): String? {
return if(lockFile.exists()) lockFile.readLines().first() else null
}
}

Related

How to define a method (of my own class) that must be invoked

I'm writing a class to manage files locking named FileLocker.
Its buildier method locks a file (which directory is received as a method's parameter) thanks to java.nio.channels.FileLock.The FileLocker class also contains a release() method that release the FileLock created in its buildier method. The problem is that the release() method have to be called, otherwise the file's lock will never be released and other processes won't be allowed to use this file (during the current JVM). So my question is: Does exist a way to indicate that release() method have to be called in all codes that use a FileLocker object, so that during those codes compilations an error is thrown if release() method isn't used? (Here I post FileLocker class code, just to show how it works):
package essentialServer_Service;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import essentialServer_Exception.EssentialServerException;
public class FileLocker implements ServerConfiguration{
String fileDirectory;
int filelockingAttempts;
int maxFilelockingAttempts;
FileChannel fileChannel;
RandomAccessFile randomAccessFile;
FileLock lock;
file f;
public FileLocker (String fileDirectory) throws EssentialServerException {
this.fileDirectory = fileDirectory;
filelockingAttempts = 0;
maxFilelockingAttempts = maxDatabaseFilesLockingAttempts();
f = new file(fileDirectory);
f.make(); // To open a channel for a specific file, this file must exist
// Note that the file.make() method throws an EssentialServerException if it seems to be impossible to create the file
fileChannel = null;
randomAccessFile = null;
try{
randomAccessFile = new RandomAccessFile(f, "rw");
fileChannel = randomAccessFile.getChannel();
}catch (FileNotFoundException e){
throw new EssentialServerException(4);
}
lock = null;
while (true) {
try{
lock = fileChannel.tryLock();
}catch (OverlappingFileLockException e){
filelockingAttempts++;
if (filelockingAttempts > maxFilelockingAttempts && maxFilelockingAttempts > -1){
try{
fileChannel.close();
randomAccessFile.close();
}catch (IOException ee){
throw new EssentialServerException(7);
}
throw new EssentialServerException(5);
}
}catch (IOException e) {
filelockingAttempts++;
if (filelockingAttempts > maxFilelockingAttempts && maxFilelockingAttempts > -1){
try{
fileChannel.close();
randomAccessFile.close();
}catch (IOException ee){
throw new EssentialServerException(8);
}
throw new EssentialServerException(6);
}
}
}
}
public void release (int ExceptionType) throws EssentialServerException{
//The int value ExceptionType indicate the EssentialServerException's type that the FileLocker class should throw if the file unlocking fails
try{
lock.release();
fileChannel.close();
randomAccessFile.close();
}catch (IOException ee){
throw new EssentialServerException(ExceptionType);
}
}
}
To directly answer your question - I do not know a way to directly enforce this. However, I would like to refer you to the AutoCloseable interface. It allows you to write code that can be used in a try-with-resources code block.
Not knowing (ignoring really) all the details of your code, using it might look like something like this:
try (FileLocker locker = new FileLocker(filename)) {
// Use locker
}
Optionally you can add your catch and finally blocks. But the point is that this guarantees that locker will be 'closed' before exiting the try-block.
Additionally, if FileLocker is only an arbitrary object that won't be further used inside the try {} block, other than releasing it again, then you could simplify this as follows:
try (new FileLocker(filename)) {
// Your code during FileLocker
}
In order for this to work, your FileLocker will have to extend AutoCloseable. Actually, if your FileLocker will throw an IOException, you can also consider extending Closeable instead. You will be required to implement the close method, which would basically have to call your release method.
Locking a file is quite simple in Java. The FileChannel (Java Doc) provides all methods we need to lock files. The tryLock() (Java Doc) method will try to obtain the lock on the file without waiting. If the lock is acquired an instance of FileLock (Java Doc) is returned, otherwise this method returns null.
FileLock is only for interprocess locking, javadoc reads:
"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."
To lock between java threads (same JVM) you need to use some shared lock. I would suggest within the file writing class to use a synchronized block (which according to these articles is likely to perform best):
An alternative approach is to use a ReentrantLock :
final ReentrantLock lock = new ReentrantLock();
public void write(...){
try {
lock.lock()
// do the writing
} finally {
// forget this and you're screwed
lock.unlock();
}
}
This case uses the lock to resolve interleaving of writes, not concurrent access.Further concurrent lock is more feature rich.However sync lock does the task as well.read doc
Both locks and reentrant locks however are mandtory to close.so new try with resource feature is handy that implements AutoCloseable interface, which is required for use with the new try-with-resource syntax. so you can write a simple wrapper like:
public class LockWrapper implements AutoCloseable
{
private final Lock _lock;
public LockWrapper(Lock l) {
this._lock = l;
}
public void lock() {
this._lock.lock();
}
public void close() {
this._lock.unlock();
}
}
Now you can write code like this:
try (LockWrapper someLock = new LockWrapper(new ReentrantLock()))
{
someLock.lock();
// ...
}

how to notify to another thread about a file being modified

I am new to the java multithreading programming. I know that it can be done by thread communication but i don't know how to proceed. I don't know how one thread would notify another if some changes are done in a file. The problem is mentioned below.
I have a comma separated file in which some lines are written. I want two threads to be started from my main thread. The csv file might be appended externally/manually. One of the thread will notify second thread if some changes are done in csv file and second thread will read that file concurrently line by line and perform some task.
Thanks.
You can use java.nio.file.WatchService for this purpose.
Refer Tutorial
From the link:-
The Watch Service API is designed for applications that need to be
notified about file change events. It is well suited for any
application, like an editor or IDE, that potentially has many open
files and needs to ensure that the files are synchronized with the
file system. It is also well suited for an application server that
watches a directory, perhaps waiting for .jsp or .jar files to drop,
in order to deploy them.
You create two thread, that inside their run method, they both use one Object as the thread wait and notify signal.
The first thread (T1), would synchronize on the object and wait on it.
The second thread (T2), would synchronize on the object, do something with it, and signal a notify.
The following snippets should give you idea... (please disregard about the endless loop and bad exception handling, its just to express the idea for ease of understanding).
public class IdeaOfThreadingWithWaitAndNotify {
public static void main(String[] args) {
File f = new File("grow.txt");
if(!f.exists()) {
try {
f.createNewFile();
Thread appenderThread = new Thread(new FileAppender(f));
Thread checkerThread = new Thread(new FileSizeCounter(f));
appenderThread.start();
checkerThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static class FileAppender implements Runnable {
private File file;
private FileOutputStream fos;
public FileAppender(File file) throws FileNotFoundException {
super();
this.file = file;
fos = new FileOutputStream(file);
}
public void run() {
while(true) {
synchronized (file) {
try {
fos.write("Appended... ".getBytes());
fos.flush();
file.notify();
Thread.sleep(1000);
} catch (IOException e) {
} catch (InterruptedException e) {
}
}
}
}
}
public static class FileSizeCounter implements Runnable {
private File file;
public FileSizeCounter(File file) {
super();
this.file = file;
}
public void run() {
while(true) {
synchronized (file) {
try {
file.wait();
} catch (InterruptedException e) {
}
System.out.println("File changed .. now size is " + file.length());
// you can do other stuff with the file...
}
}
}
}
}
there you can see, between the two thread, they are sharing the same "file" instance and use it as the wait and notify signaling. Object who call the wait will have its execution flow stop right there, until the other thread call a notify on it. Then the waiting thread can continue.
I hope this helps.

Single program instance

I need to make a program, which can be executed in single instance. I tried to create a temporary file and delete it before exit program.
public static boolean isLocked() {
File f = new File("lock.txt");
return f.exists();
}
public static void lock() {
String fname = "lock.txt";
File f = new File(fname);
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unlock() {
File f = new File("lock.txt");
f.delete();
}
In frame
private void initialize() {
lock();
}
private void setFrameHandler() {
frame.addWindowListener(new java.awt.event.WindowAdapter() {
#Override
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
unlock();
}
});
}
Problem occurs if program is finished with emergency (e.g. electricity cuts). File does not remove, and running a new instance is impossible.
How to make a reliable single-instance verification?
You could check for another instance of the program at startup using the GetProcesses method as described here
But that only works depending on the scenario you have (might not see all processes of other users)
Another thing you could do is simply checking, if a specific file is locked via File.Open
File.Open ("path.lock", FileMode.OpenOrCreate, FileAccess.ReadWrite);
As long as you keep the resulting FileStream open in your program no other program can open the file in that mode either. This is basically how Unix lock files work too. Of course you have to catch an IOException (hinting you to a locked file).
Disclaimer: I did not try that code out so please check if I gave you the right parameters.
Edit: You could also check out this Code-Project article on how to do it with the win32 API
Another attempt using windows messaging has been done here
A simple approach to this on a single machine is to write a 'PID file', which is literally a file containing the operating system's ID of the process currently running. You create this when you start your "critical" work, and remove it on successful completion.
Since it is unlikely that the process would be started again with the same PID, you can simply check to see if the PID file already exists, and if so, if that process is still running.

Workaround for Java bug which causes crash dump

A program that I've developed is crashing the JVM occasionally due to this bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8029516. Unfortunately the bug has not been resolved by Oracle and the bug report says that there are no known workarounds.
I've tried to modify the example code from the bug report by calling .register(sWatchService, eventKinds) in the KeyWatcher thread instead, by adding all pending register request to a list that I loop through in the KeyWatcher thread but it's still crashing. I'm guessing this just had the same effect as synchronizing on sWatchService (like the submitter of the bug report tried).
Can you think of any way to get around this?
From comments:
It appears that we have an issue with I/O cancellation when there is a pending ReadDirectoryChangesW outstanding.
The statement and example code indicate that the bug is triggered when:
There is a pending event that has not been consumed (it may or may not be visible to WatchService.poll() or WatchService.take())
WatchKey.cancel() is called on the key
This is a nasty bug with no universal workaround. The approach depends on the specifics of your application. Consider pooling watches to a single place so you don't need to call WatchKey.cancel(). If at one point the pool becomes too large, close the entire WatchService and start over. Something similar to.
public class FileWatcerService {
static Kind<?>[] allEvents = new Kind<?>[] {
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
};
WatchService ws;
// Keep track of paths and registered listeners
Map<String, List<FileChangeListener>> listeners = new ConcurrentHashMap<String, List<FileChangeListener>>();
Map<WatchKey, String> keys = new ConcurrentHashMap<WatchKey, String>();
boolean toStop = false;
public interface FileChangeListener {
void onChange();
}
public void addFileChangeListener(String path, FileChangeListener l) {
if(!listeners.containsKey(path)) {
listeners.put(path, new ArrayList<FileChangeListener>());
keys.put(Paths.get(path).register(ws, allEvents), path);
}
listeners.get(path).add(l);
}
public void removeFileChangeListener(String path, FileChangeListener l) {
if(listeners.containsKey(path))
listeners.get(path).remove(l);
}
public void start() {
ws = FileSystems.getDefault().newWatchService();
new Thread(new Runnable() {
public void run() {
while(!toStop) {
WatchKey key = ws.take();
for(FileChangeListener l: listeners.get(keys.get(key)))
l.onChange();
}
}
}).start();
}
public void stop() {
toStop = true;
ws.close();
}
}
I've managed to create a workaround though it's somewhat ugly.
The bug is in JDK method WindowsWatchKey.invalidate() that releases native buffer while the subsequent calls may still access it. This one-liner fixes the problem by delaying buffer clean-up until GC.
Here is a compiled patch to JDK. In order to apply it add the following Java command-line flag:
-Xbootclasspath/p:jdk-8029516-patch.jar
If patching JDK is not an option in your case, there is still a workaround on the application level. It relies on the knowledge of Windows WatchService internal implementation.
public class JDK_8029516 {
private static final Field bufferField = getField("sun.nio.fs.WindowsWatchService$WindowsWatchKey", "buffer");
private static final Field cleanerField = getField("sun.nio.fs.NativeBuffer", "cleaner");
private static final Cleaner dummyCleaner = Cleaner.create(Thread.class, new Thread());
private static Field getField(String className, String fieldName) {
try {
Field f = Class.forName(className).getDeclaredField(fieldName);
f.setAccessible(true);
return f;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static void patch(WatchKey key) {
try {
cleanerField.set(bufferField.get(key), dummyCleaner);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
Call JDK_8029516.patch(watchKey) right after the key is registred, and it will prevent watchKey.cancel() from releasing the native buffer prematurely.
You might not be able to work around the problem itself but you could deal with the error and handle it. I don't know your specific situation but I could imagine the biggest issue is the crash of the whole JVM. Putting all in a try block does not work because you cannot catch a JVM crash.
Not knowing more about your project makes it difficult to suggest a good/acceptable solution, but maybe this could be an option: Do all the file watching stuff in a separate JVM process. From your main process start a new JVM (e.g. using ProcessBuilder.start()). When the process terminates (i.e. the newly started JVM crashes), restart it. Obviously you need to be able to recover, i.e. you need to keep track of what files to watch and you need to keep this data in your main process too.
Now the biggest remaining part is to implement some communication between the main process and the file watching process. This could be done using standard input/output of the file watching process or using a Socket/ServerSocket or some other mechanism.

race condition with file getAbsolutePath() java

It seems like I have a race condition when I call file.getAbsolutePath() in my Java program.
In one thread I am processing a file and when it is finished processing I am changing the filename and moving it to another directory on the UNIX file system.
In a separate thread running in parallel I am attempting to open this file that is being processed and reading its contents. In 99% of use cases this operation is fine however I have noticed sometimes that the operation fails with a FileNotFound exception.
When I catch this exception I am logging the file.getAbsolutePath() value and I see the value is the concatenation of the path of the file in the processed directory it has been moved to and also the path of the file in the directory it was present in before processing completed.
Has anyone experienced a similar problem in the past and how did you get around it?
Thanks
It seems you need to synchronize the file access from separate threads using a class that does this, let's call it FileManager.
First option in implementing the FileManager is to use an exclusive lock. For example:
class FileManager {
private Object lock = new Object();
public void processFile() {
synchronized(lock) {
...
}
}
public void readFile() {
synchronized(lock) {
...
}
}
}
If there are many more readers than writers a Read/Write Lock is more suitable as it allows multiple concurrent readers but only a single writer:
class FileManager {
private final Lock readLock;
private final Lock writeLock;
FileManager() {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(false);
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
}
public void processFile() {
writeLock.lock();
try {
...
}
finally {
writeLock.unlock();
}
}
public void readFile() {
readLock.lock();
try {
...
}
finally {
readLock.unlock();
}
}
}

Categories