【问题标题】:How can dynamic JVM command line flags be passed to a self-contained JavaFX application?如何将动态 JVM 命令行标志传递给自包含的 JavaFX 应用程序?
【发布时间】:2015-06-12 17:52:55
【问题描述】:

编辑:Oracle 接受我的错误报告,要求以JDK-8138944 : Support command line arguments to the JVM passed to self-contained app launchers 进行增强。

问题

我的团队正在开发一个开源 Java SE 项目 ImageJ,该项目目前有一个用跨平台 C 编写的 custom native launcher。我们希望摆脱这个启动器,转向更现代的行业标准和可维护的部署机制。 JavaFX self-contained applications 是迄今为止我们发现的最有希望的解决方案。

ImageJ 当前本机启动器的一个重要特性是它能够自定义 JVM 的启动方式。例如,你可以写:

ImageJ --debugger=8000 myFile.png

启动器将调用带有标志的JVM:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:8000

同时将 myFile.png 作为 Java 主类的参数。

但从文档中,我们看不到使用 JavaFX 打包工具完成类似任务的方法。

注意事项

我知道UserJvmOptionsService 提供了一种配置 JVM 启动方式的方法(通过内部的 Java Preferences API,JavaFX 启动器在 JVM 启动之前读取该 API)。这非常适合为用户提供一个友好的对话框,他们可以在其中调整最大堆大小和其他常用参数。当然,我们可以在这样的对话框中添加远程调试开关和端口设置,和/或通过 CLI 支持 JVM 配置——但是这需要重新启动应用程序。

理想情况下,我们根本不需要支持这种情况,而是在 JVM 启动后用 Java 处理 all 命令行参数。例如,在大多数情况下,只需在运行时解析 arg 并设置系统属性即可支持-Dfoo=bar 形式的系统属性,只要它在应用程序的启动周期中足够早地完成即可。但显然有很多情况在 JVM 启动之后再做为时已晚:

我们的用户希望能够在 CLI 上传递这些设置,并让 Java 运行时相应地运行 - 对于 ImageJ,向后兼容性尤为重要。

可能的解决方案

  • 我们可以保留本机 C 启动器,替换 Java 打包工具安装的本机可执行文件。但这让我觉得非常脆弱,并且在很大程度上违背了切换到 JavaFX 部署的目的,因为我们仍然需要跨多个不同平台维护、构建和测试自定义本机启动器。

  • 或者,我们可以让应用程序主类成为一个非常薄的 CLI 选项解析器,然后生成 JVM 的第二个实例。这将使引导逻辑保持在纯 Java 中,这将比当前的本机 C 代码更易于维护,同时充分利用 JavaFX 部署方案的跨平台捆绑功能。但这似乎是一个具有潜在挑战性副作用的大技巧。

最后,问题

是否有人通过 JavaFX 自包含应用程序部署实现了对 JVM CLI 参数的支持?如果有,您是如何实现的?如果没有,还有其他建议吗?

【问题讨论】:

    标签: java deployment javafx jvm


    【解决方案1】:

    您可以通过修改 jvm 用户覆盖 API 写入的配置文件来修改 JVM 的启动参数:

    • Mac ~/Library/Application Support/[app.preferences.id]/packager/jvmuserargs.cfg • Windows C:\Users[用户名]\AppData\Roaming[app.preferences.id]\packager\jvmuserargs.cfg • Linux ~/.local/[app.preferences.id]/packager/jvmuserargs.cfg

    注意:这是一个内部实现细节,可能会发生变化。

    【讨论】:

    • 谢谢克里斯!我看到您也评论了我的 OpenJDK 问题报告 (bugs.openjdk.java.net/browse/JDK-8138944)。所以,我猜这些是您使用UserJvmOptionsService 时修改的文件?这很好,但在 Java 方面,使用公共 API 对我来说更有意义,而不是重新发明轮子并依靠内部实现细节。无论哪种方式:您是否不必启动 JVM 来进行修改,然后重新启动 JVM 以使新设置生效?我的要求是在不重启 JVM 的情况下做到这一点。
    • 我想直接在 OpenJDK 板上发表评论以继续讨论(尤其是因为您写了“欢迎反馈”:-D),但我不知道如何为该 JIRA 创建帐户。所以,在这里非常简单:我担心 -X 标志的特殊大小写会导致各种问题和限制 - 例如,如果应用程序使用类似 JVM 的标志运行,它本身可能希望接收 -Xmx 和类似的。我的“--”分隔符提案提供了执行此操作的能力。另一种方式是例如Maven 的“argLine”技巧 (maven.apache.org/surefire/maven-surefire-plugin/…)。
    • 所以你在想所有的——参数都是作为 JVM args 传递的?或者有@{-Xmx256M}?我的提议可能会引起冲突,同意。我可以劫持发射器理解的少数标志。实际上,这可能就是它的样子,因为启动器理解的任何参数也需要知道如何在 JVM 用户覆盖中潜在地覆盖它。它变得复杂。无论哪种方式,参数的数量都会受到限制。 -Xmx,-Xdebug,你还希望启动器知道什么?
    • 我还应该提到,如果参数在 Java 平台上是一致的,那么它是首选。我最近在 JDK9 中添加了对“javapackager -Xmx”的支持。让它有所不同会有点奇怪,但我也不想干扰应用程序期望的参数。这就是为什么我们没有为 8u40 这样做。
    • 我的建议是您可以编写例如:myImageViewer -Xmx512m -Xdebug -- myFile.png-- 分隔符将明确地将 JVM 参数与应用程序端参数分开,JVM 参数首先出现。这种行为与 *nix 实用程序具有相同的精神,后者使用-- 在程序标志和文件参数之间进行划分。我也很喜欢你的 @{...} 语法——这更接近于 Maven argLine 的编写方法,例如myImageViewer -DargLine="-Xmx512m -Xdebug" myFile.png 并让启动器将该系统属性值作为额外的 JVM 参数。
    【解决方案2】:

    我建议您通过复制和编辑平台 java.c 启动器来创建自己的本机启动器,而不是使用任何类型的两阶段启动系统,您可以在其中查看您有哪些选项然后启动替代 JVM。你可以在打开的 JDK 项目中看到他们在做什么。

    http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/914cd9b482c8/src/share/bin/java.c

    他们在很多地方寻找各种选项并将它们转换为 JVM 本身的初始化参数。看看 ParseArguments、AddApplicationOptions、TranslateApplicationArgs、CheckJvmType、AddOption 等函数。

    我不是 C 程序员,但我在很多场合都维护过自己的启动器:例如从每行一个条目的文本文件加载特定类路径的启动器,该文件可以签入源代码控制和一个使用不同入口点的 main()。它并没有太大的变化,您可以很容易地隔离您的更改,以便它们很容易在更高版本的 java.c 上重新应用。出于您的目的,您不需要在每次有人对 java.c 进行更改时都进行更改,您只需要在 JavaVMInitArgs 或调用接口的其他一些关键方面发生更改时进行更改。

    我敢肯定,如果您提出并贡献了一个更优雅的选项处理解决方案,也许当 argv[0] 不是“java”时表现不同,那么开放 jdk 团队可能会采用您的方法并维护它你,或者我们所有人。我相信有很多开发人员需要这些功能。

    【讨论】:

    • 谢谢,胡安·卡洛斯。正如我在上面的问题中所描述的,我们实际上已经有一个原生启动器,但是它太复杂了。我想我们可以从一个新的、非常简单的启动器重新开始,但理想情况下,我想完全避免维护 any 本机代码,这正是这个问题的真正意义所在。例如,用户配置启动哪个 JVM 的需求在许多 Java 应用程序中是否很常见?向 OpenJDK 提交补丁是个好主意。也许这是去这里的最佳方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-13
    • 2010-09-30
    • 1970-01-01
    • 2015-06-08
    相关资源
    最近更新 更多