I'm developing a Java application. And I wanted to prevent a user to run simultaneously more instances of the same Java application. I used JUnique app locking library for Java and it works great. But has a serious issue when it crashes.
The application cannot be started if it crashes, it just returns AlreadyLockedException. The code I used to lock my application is below.
public static boolean isRunning() {
boolean alreadyRunning = false;
try {
JUnique.acquireLock(appId);
alreadyRunning = false;
} catch (AlreadyLockedException e) {
logger.error("Unable to acquire lock. There is an instance already running");
alreadyRunning = true;
} catch (Throwable t) {
logger.error("Unable to acquire lock. ", t);
}
return alreadyRunning;
}
And code to release my lock is:
public static void release() {
try {
JUnique.releaseLock(appId);
} catch (Throwable t) {
logger.error("Error releasing the lock", t);
}
}
I can use release() method for dealing with expected crashes. But the real problem occurs when application crashes unexpectedly during runtime. The application is terminated without releasing the acquired lock for application.
How can we release JUnique lock if the application unexpectedly crashes?
It is a design flaw of JUnique (last version I found here). There is simply no support for the use case of a computer crash (BSOD, power interrupt). It is assumed that the ShutdownHook
(which removes the locks, see JUnique.java) is always executed. The flaw is evident in the releaseLock
api-docs:
"Please note that a lock can be realeased only by the same JVM that has previously acquired it. If the given ID doens't correspond to a lock that belongs to the current JVM, no action will be taken."
But there is no method forceReleaseLock(String id)
which in turn means that after a computer crash, the application can no longer be started.
In these kind of cases it is quite common to present the user a dialog with a message "The application is already running" and an "OK" button that shows the window of the running application. In this case, you could add a button "No, it is not" and when clicked:
JUnique.sendMessage
returns null.new File(System.getProperty("user.home"), ".junique");
(the LOCK_FILES_DIR, see JUnique.java), or something similar that is less crude.