【问题标题】:jar file not up updating values in filejar 文件没有更新文件中的值
【发布时间】:2016-12-31 18:57:40
【问题描述】:

我有一个 javafx 项目,我正在为分发做准备。该项目可以在 NetBeans 内的调试器中完美运行。我在 Linux 上工作。

但是当我从 jar 文件运行项目时,所有其他功能中都有一个不起作用。此功能应该在单击按钮时打开设置文件并将某些值从 true 更改为 false。

我已经更改了设置文件的位置并尝试了谷歌,但都无济于事。

我还是 java、fx、netbeans 和 java 的新手(没有那么多编程),并且是我的第一次体验。

知道为什么会这样吗?

@FXML
private void openSettingsFile(ActionEvent event) throws IOException {

    // this test works ....
    ProcessBuilder processBuilder = new ProcessBuilder("terminal");
    processBuilder.start();

    // this part only replaces the values when I use the debugger ..
    Path path = Paths.get("src/desktop_launcher/Settings.java");
    Charset charset = StandardCharsets.UTF_8;
    String content = new String(Files.readAllBytes(path));
    content = content.replaceAll(" \"true\"" , " \"false\"");
    Files.write(path, content.getBytes(charset));

【问题讨论】:

  • 你想在这里做什么?看起来您正在尝试操作 source 文件并使其在运行时生效。您如何编译文件、构建 jar 并在运行时动态重新加载类?除此之外,您不能指望src 目录在运行时可用。你知道Java是编译的吧??
  • 我有一系列复选框,每次我关闭和重新加载程序时,它们都应该记住它们的状态是被选中(或未被选中)。当我关闭应用程序并更改复选框状态时,它应该以关闭的方式启动。我创建了一个设置文件,上面的函数应该提供设置文件,作为回报,提供复选框的初始状态。我知道设置文件的位置也不理想。但是尝试更改文件夹并没有帮助。文件位置对于函数的工作是否重要?它可以在调试器中正常工作
  • 如果是设置文件,为什么会有.java 文件扩展名?您应该使用文本文件,格式可能与java.util.Properties 兼容。至于文件位置,src 文件夹在运行时显然不可用,Paths.get(...) 将相对于工作目录进行解析。我建议使用用户主目录 (System.getProperty("user.home")) 并在其中创建一个目录和文件。
  • 其实不是设置文件,是创建属性文件的文件....mkyong.com/java/java-properties-file-examples
  • 所以你正在尝试重写一个Java源文件???

标签: debugging javafx jar executable-jar


【解决方案1】:

由于多种原因,您的方法(据我所知,它是尝试以编程方式更改生成属性文件的源文件)在部署时不起作用。

首先是源文件在运行时通常不可用:您的 jar 文件包含运行应用程序所需的类文件和其他资源,但通常不包含源代码(并且不希望将其包含在应用程序中, 一般来说)。

其次,您试图从传递给Paths.get(..) 的相对路径中找到此文件。这将相对于工作目录进行解析,工作目录本质上是任意的(基本上是“应用程序的运行位置”)。因此,即使源代码在运行时可用,这也不是找到它的可靠方法。 (我的猜测是你的调试器运行时工作目录偶然设置为src的父目录,但是当你运行jar文件时,工作目录最有可能的位置是jar文件所在的目录。但是这只是一个猜测:它实际上取决于您的 IDE、调试器等的配置。)

第三,可能也是最重要的一点,即使代码确实找到了源文件并重写了它,这也是它所能做的。下次您从 jar 文件执行应用程序时,它不会神奇地知道有一个新版本的源代码需要编译,然后将生成的类文件合并到 jar 文件中。因此,您还必须包含代码来编译源代码的新版本(您将在哪里获得编译器?AFAIK 并非所有 Java 运行时都包含编译器),然后以编程方式将新的类文件插入到 jar 文件中(您甚至如何弄清楚 jar 文件在哪里:这当然不是微不足道的,我认为它不能以可靠的方式完成)。如果当前用户没有权限写入包含 jar 的目录(这是一种非常常见的情况..)怎么办?

加载和保存启动配置值的常用方法是使用java.util.Properties API。您需要一个外部位置来存储属性文件,您可以确定它存在于用户的计算机上:一种方便的方法是在用户的主目录中创建一个特定于应用程序的目录。可以通过System.getProperty("user.home"); 访问用户的主目录(系统属性user.homeguaranteed to exist 之一。)。

我建议使用单独的类来管理配置属性。例如:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Properties;

public class PropertiesAccessor {

    private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));

    private Properties props ;
    private Path path ;

    public PropertiesAccessor(String relativePath) {
        path = USER_HOME.resolve(relativePath);

        props = new Properties();

        if (Files.exists(path)) {
            try {
                props.load(Files.newBufferedReader(path));
            } catch (IOException exc) {
                System.err.println("Warning: could not load properties file. Using defaults.");
                exc.printStackTrace(System.err);
                loadDefaults();
            }
        } else {
            loadDefaults();
        }

    }

    public Boolean getBooleanValue(String key) {
        String value = props.getProperty(key);
        return value == null ? null : Boolean.valueOf(value) ;
    }

    public void updateBooleanValue(String key, boolean value) {
        props.setProperty(key, Boolean.toString(value));
    }

    public void writeProperties() throws IOException {
        if (! Files.exists(path)) {
            Files.createDirectories(path.getParent());
            Files.createFile(path);
        }
        props.store(Files.newBufferedWriter(path), "Properties updated "+LocalDateTime.now());
    }

    private final void loadDefaults() {
        // in real life, you might keep a default properties file bundled with
        // the application and read that here, e.g. 
        // props.load(getClass().getResourceAsStream("/default-startup.properties"));

        props.setProperty("config.value1", "true");
        props.setProperty("config.value2", "false");
    }
}

现在您可以在您的应用程序中使用它。只需在init() 方法中加载属性并将它们保存回stop() 方法中。 请注意,执行此操作将在您的主目录中创建一个名为 .myApp 的目录,并在其中创建一个名为 startup.properties 的文件。

import java.io.IOException;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class StartupPropertiesExample extends Application {

    private PropertiesAccessor config ;

    private CheckBox value1 ;
    private CheckBox value2 ;

    @Override
    public void init() {
        config = new PropertiesAccessor(".myApp/startup.properties");
    }

    @Override
    public void start(Stage primaryStage) {
        value1 = new CheckBox("Value 1");
        value2 = new CheckBox("Value 2");
        value1.setSelected(config.getBooleanValue("config.value1"));
        value2.setSelected(config.getBooleanValue("config.value2"));

        Button exit = new Button("Exit");
        exit.setOnAction(e -> Platform.exit());

        VBox root = new VBox(10, value1, value2, exit);
        root.setPadding(new Insets(10));

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void stop() {
        config.updateBooleanValue("config.value1", value1.isSelected());
        config.updateBooleanValue("config.value2", value2.isSelected());
        try {
            config.writeProperties();
        } catch (IOException exc) {
            System.err.println("Warning: could not save properties");
            exc.printStackTrace(System.err);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

【讨论】:

  • James,非常感谢您提供宝贵的技术建议。您的观察确实回答了我对 java 基础的很多问题......(其他人来......)。我已经尝试过使用其他文件夹位置的 Get.Path 方法,但它似乎确实效果不佳。我对正确存储和检索设置值的主要兴趣变得清晰,并且解决方案非常优雅。非常感谢您花时间和耐心调查此事并做出如此广泛的回答。
  • 詹姆斯,为什么是“config.updateBooleanValue("config.value1", value1.isSelected());"这一行仅在停止方法中工作?
  • @Rainer 不确定我是否理解这个问题。 stop 方法中的代码使用来自 UI 的新值更新属性并将它们保存到属性文件中。在程序退出之前,您不需要这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-12
  • 2017-12-02
  • 2015-11-12
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 2011-09-17
相关资源
最近更新 更多