每个用户具有单个实例的Java应用程序

时间:2018-10-22 14:24:14

标签: java java-8 install4j

当前,我在为使用 install4j 打包到.exe中的单实例JavaFX应用程序而苦苦挣扎。该应用程序应在Windows终端服务器上运行,并且每个用户只能运行一个实例。意思是, Alice Bob 可以使用该应用程序的单独实例,但是 Alice 只能打开一个实例。

写一个带有进程ID的锁文件不是一个可行的选择,因为该应用程序以Java 8为目标,因此没有一致的可能性来检索进程ID。打开套接字也不是理想的解决方案,因为同一主机上可能有很多实例。此外,我想如果某些应用程序在服务器上随机打开套接字,管理员不会感到高兴...

当我使用 install4j 打包应用程序时,我切换了“仅单个实例”功能,当通过完整的RDP会话连接时,该功能似乎运行良好。但是,可以使用RemoteApp功能部署该应用程序,该功能在某种程度上规避了 install4j 的检查机制,从而允许一个实例在RDP会话中启动,而另一个实例可以通过RemoteApp启动。

这使我想到两个问题:

  1. install4j如何检查工作? (我找不到任何详细信息...)
  2. 什么是始终确保每个用户一个实例的最佳解决方案? (并且还具有故障保护功能,例如从JVM崩溃中恢复)
  3. 关于FileLock的可能性:由于不同的操作系统对文件锁的处理方式可能不同,是否可以确定文件锁是由整个系统上的一个JVM实例专有获得的?

3 个答案:

答案 0 :(得分:1)

如果您希望应用程序在不同的用户下并发运行,则套接字会有些问题。

可以使用NIO FileLock。您在用户目录下创建文件,以便另一个用户可以拥有自己的锁定文件。此处的关键是仍然尝试通过重新创建之前尝试将其删除来获取文件锁(如果该文件已存在)。这样,如果应用程序崩溃并且文件仍然存在,您仍将能够获得对其的锁定。请记住,当进程终止时,操作系统应释放所有锁,打开的文件句柄和系统资源。

类似这样的东西:

 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);
    }
  }
}

另一种替代方法是使用像Junique一样为您执行此操作的库。我自己还没有尝试过,但是您可以尝试一下。看起来很老了,但我想这样的事情没有什么需要改变的,自Java 1.4以来,NIO并没有太大的改变。

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

尽管它在Maven Central上,所以您可以轻松导入它。 https://mvnrepository.com/artifact/it.sauronsoftware/junique/1.0.4

如果您查看代码,将会发现它与文件锁定具有相同的作用: https://github.com/poolborges/it.sauronsoftware.junique/blob/master/src/main/java/it/sauronsoftware/junique/JUnique.java

答案 1 :(得分:0)

关于1:在Windows上,install4j启动器使用Windows API中的CreateSemaphore函数创建一个信号灯。您可以通过使用

从命令行执行启动程序来检查信号量的名称。
/create-i4j-log

参数。

答案 2 :(得分:0)

我遇到了同样的问题,并像其他答案一样使用FileLock解决了这个问题。

就我而言,传递给启动的进程的参数需要转发给第一个进程。为此,我使用了一个命名管道,该管道的名称中包含用户名。第一个过程在\。\ pipe \ app_ $ USER中创建命名管道。如果同一用户启动了同一exe,则FileLock会检测到该exe,并将这些摘要通过命名管道传递。