Java Application with Single Instance per User

Rosso :

currently I am struggling with the problem of a single instance JavaFX application, packed into an .exe using install4j. The application should run on a Windows terminal server and every user should only be able to run one instance of it. Meaning, Alice and Bob may use separate instances of the application but Alice may only have one instance open.

Writing a lock file with the process id is not a viable option, since the application is targed at Java 8, which has no consistent possibility to retrieve the process id. Opening a socket is also not a desirable solution, as there can be many instances on the same host. Moreover I suppose admins would not be that happy if some application randomly opened sockets on their server...

As I am using install4j to pack the application, I toggled the 'single instance only' feature which seems to run well when connected via a full RDP session. However, the application may be deployed using the RemoteApp feature which in some way circumvents install4j's checking mechanism, allowing one instance to be launched in a RDP session and another by using the RemoteApp.

This leads me to two questions:

  1. How does the install4j check work? (I was not able to find any details...)
  2. What would be the best solution to ensure a single instance per user at all times? (And also be failsafe, e.g. recover from JVM crashes)
  3. Regarding the possibility of FileLock: as different operating system may handle file locks differently, can it be assured that the file lock is exclusively acquired by one JVM instance on the whole system?
jbx :

Sockets will be a bit problematic if you want the application to run concurrently under different users.

The option of using an NIO FileLock is possible. You create the file under the user's directory so that another user can have his own lock file. The key thing to do here is to still try to acquire the file lock if the file exists already, by attempting to delete it before recreating it. This way if the application crashes and the file is still there, you will still be able to acquire a lock on it. Remember that the OS should release all locks, open file handles and system resources when a process terminates.

Something like this:

 public ExclusiveApplicationLock
     throws Exception {

   private final File file;
   private final FileChannel channel;
   private final FileLock lock;

   private ExclusiveApplicationLock()  {

       String homeDir = System.getProperty("user.home");

       file = new File(homeDir + "/.myapp", app.lock");
       if (file.exists()) {
          file.delete();
       }

       channel = new RandomAccessFile(file, "rw").getChannel();
       lock = channel.tryLock();
       if (lock == null)  {
          channel.close();
          throw new RuntimeException("Application already running.");
       }

       Runtime.getRuntime().addShutdownHook(new Thread(() -> releaseLock());            
  }

  private void releaseLock() {
    try {
      if (lock != null) {
        lock.release();
        channel.close();
        file.delete();
      }
    }
    catch (Exception ex) {
       throw new RuntimeException("Unable to release application process lock", ex);
    }
  }
}

Another alternative is to use a library that does this for you like Junique. I haven't tried it myself but you could have a go. It seems very old but I guess there isn't much that needs to change in something like this, nothing much changed in NIO since Java 1.4.

http://www.sauronsoftware.it/projects/junique/

It is on Maven Central though so you can import it easily. https://mvnrepository.com/artifact/it.sauronsoftware/junique/1.0.4

If you look at the code you will see that it does the same thing with file locks: https://github.com/poolborges/it.sauronsoftware.junique/blob/master/src/main/java/it/sauronsoftware/junique/JUnique.java

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=107315&siteId=1