JavaFX 应用程序在打开时覆盖文件会导致异常

JavaFX app overwriting files while being open causes exception

我有一个 运行nable jar 文件(带有一个包含所有依赖项 jar 的 lib 文件夹)。它位于网络共享上,任何有权访问的人都可以 运行 从中访问。除了一个巨大的警告外,这很好用。如果我要部署一个新版本的软件,我必须要求大家先退出应用程序。这是因为如果我用新版本覆盖 jar(或者如果有网络故障),运行ning 程序会保持打开状态,但是一旦它们执行需要依赖项代码的操作(jar 文件在lib文件夹),会导致异常:

Exception in thread "JavaFX Application Thread" java.lang.NoClassDefFoundError

程序不会产生错误,但某些操作会中断,例如与 API 通信等

有什么方法可以解决这个问题,这样我就可以在用户工作时发布更新,或者至少产生一个提示,迫使他们close/and 重新打开应用等

一种方法:

  1. 提供从远程代码的本地副本启动应用程序的脚本。
  2. 在您的应用中存储版本号。
  3. 脚本检查机器上是否有应用程序的本地副本。
    • 如果不存在本地版本,脚本会将 jar 从您的网络共享复制到本地副本。
    • 如果已经有本地副本,它会根据网络版本检查版本。
      • 如果更新了网络版本,它会在启动应用程序之前用新的远程版本覆盖本地副本,
      • 否则它只会启动本地副本。

如果您希望提醒用户他们当前是 运行 一个过时的副本,您可以创建一个 JavaFX 任务来轮询远程版本号并对照当前 运行 版本进行检查数字。如果它们不同,您可以提醒并(如果您愿意)关闭应用程序和 re-trigger 启动器脚本。

我能够创建一个方案,其中我有 2 个服务器文件夹位置,用于存放可分发的 jar。这个 jar 基本上会检查这两个位置以获取应用程序的最新副本并运行该最新副本。通过检测 OS.

,我能够让它同时适用于 Mac 和 Windows(未测试 Linux)

所以现在,我可以在最旧的应用程序上发布更新,下次用户打开应用程序时,它将是最新的副本。

process.properties

location.a=Application/A
location.b=Application/B
app=app.jar

Main.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

public class Main
{

    public static Properties properties;
    private static final String DEFAULT_PROPERTY_FILE_LOCATION = Paths.get("").toAbsolutePath().toString() + File.separator + "process.properties";
    private static final String JAVE_EXEC;

    static
    {
        String os = System.getProperty("os.name");

        if (StringUtils.containsIgnoreCase(os, "win"))
        {
            JAVE_EXEC = "java";
        } else if (StringUtils.containsIgnoreCase(os, "mac"))
        {
            JAVE_EXEC = "/usr/bin/java";

        } else if (StringUtils.containsIgnoreCase(os, "nux") || StringUtils.containsIgnoreCase(os, "nix"))
        {
            JAVE_EXEC = "/usr/bin/java";
        } else
        {
            JAVE_EXEC = "java";
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {

        Main.properties = new Properties();

        try
        {

            InputStream in = new FileInputStream(DEFAULT_PROPERTY_FILE_LOCATION);
            Main.properties.load(in);
            System.out.println("Loaded property file: " + DEFAULT_PROPERTY_FILE_LOCATION);

            if (validateProperties(properties))
            {

                String networkLocation1 = Paths.get("").toAbsolutePath() + File.separator + Main.properties.getProperty("location.a");
                String networkLocation2 = Paths.get("").toAbsolutePath() + File.separator + Main.properties.getProperty("location.b");
                String appName = Main.properties.getProperty("app");

                if (FileUtils.lastModified(new File(networkLocation1 + File.separator + appName)) > FileUtils.lastModified(new File(networkLocation2 + File.separator + appName)))
                {
                    Runtime.getRuntime().exec(new String[]
                    {
                        JAVE_EXEC, "-jar", networkLocation1 + File.separator + appName
                    }, null, new File(networkLocation1));
                } else
                {
                    Runtime.getRuntime().exec(new String[]
                    {
                        JAVE_EXEC, "-jar", networkLocation2 + File.separator + appName
                    }, null, new File(networkLocation2));

                }

            }
        } catch (IOException ex)
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private static boolean validateProperties(Properties properties)
    {
        List<String> mandatoryProperties = new ArrayList<>();

        mandatoryProperties.add("location.a");
        mandatoryProperties.add("location.b");
        mandatoryProperties.add("app");

        for (String mandatoryProperty : mandatoryProperties)
        {
            if (properties.get(mandatoryProperty) == null)
            {
                System.out.println("Failed - Property: " + mandatoryProperty + " doesn't exist.");
                return false;
            }
        }
        return true;

    }

}