5

EDIT: Oracle accept my bug report requesting an enhancement as JDK-8138944 : Support command line arguments to the JVM passed to self-contained app launchers.

The problem

My team is working on an open source Java SE project, ImageJ, that currently has a custom native launcher written in cross-platform C. We would like to move away from this launcher, switching to a more modern, industry standard and maintainable deployment mechanism. JavaFX self-contained applications are the most promising solution we have found so far.

One great feature of ImageJ's current native launcher is its ability to customize how the JVM is launched. For example, you can write:

ImageJ --debugger=8000 myFile.png

And the launcher will invoke the JVM with flag:

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

while keeping the myFile.png as an argument to the Java main class.

But from the docs, we cannot see a way to accomplish something similar with the JavaFX packaging tool.

Considerations

I know that the UserJvmOptionsService provides a means to configure how the JVM is started (via Java Preferences API internally, which the JavaFX launcher reads before JVM startup). This is nice for providing the user with a friendly dialog box where they can tweak maximum heap size and other common parameters. Certainly we could add a remote debugging toggle and port settings to such a dialog box, and/or support JVM configuration via the CLI—but it would then require a restart of the application.

Ideally we would not need to support this scenario at all, and instead would handle all command line arguments after the JVM starts up, in Java. For example, in most cases, system properties of the form -Dfoo=bar can be supported by simply parsing the arg and setting the system property at runtime, as long as it is done early enough in the application's startup cycle. But there are clearly many cases where doing it after JVM startup is too late:

Our users expect to be able to pass these settings on the CLI, and have the Java runtime operate accordingly—and in the case of ImageJ, it is particularly important for backward compatibility.

Possible solutions

  • We could retain the native C launcher, replacing the native executable that the Java packaging tool installs. But this strikes me as highly fragile, and largely defeats the purpose of switching to JavaFX deployment, since we would still need to maintain, build and test the custom native launcher across several different platforms.

  • Alternately, we could have the application main class be a very thin CLI option parser, which then spawns a second instance of the JVM. This would keep the bootstrapping logic in pure Java, which would be far more maintainable than the current native C code, while fully leveraging the JavaFX deployment scheme's cross-platform bundling capabilities. But that seems like a big hack with potentially challenging side effects.

Finally, the question

Has anyone achieved support for JVM CLI arguments with JavaFX self-contained application deployment? If so, how did you accomplish it? If not, any alternative suggestions?

Community
  • 1
  • 1
ctrueden
  • 6,751
  • 3
  • 37
  • 69

2 Answers2

1

You can modify the launch arguments to the JVM by modifying the jvm user overrides config file that the API writes to:

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

NOTE: This is an internal implementation detail and subject to change.

Chris Bensen
  • 271
  • 2
  • 3
  • Thanks Chris! I see that you commented on my OpenJDK issue report as well (https://bugs.openjdk.java.net/browse/JDK-8138944). So, I am guessing these are the files which are modified when you use the `UserJvmOptionsService`? That's fine, but on the Java side, it makes more sense to me to use the public API rather than reinventing the wheel and leaning on internal implementation details. And either way: wouldn't you have to spin up a JVM to make the modifications, and then restart the JVM so the new settings take effect? My request is for a way to do this without a JVM restart. – ctrueden Mar 23 '16 at 15:52
  • 1
    I would like to comment on the OpenJDK board directly to continue the discussion (especially since you wrote "Feedback welcome" :-D) but I do not see how to create an account for that JIRA. So, very briefly here: I am concerned that special casing the -X flags will cause various problems and limitations—e.g., if an application operates with JVM-like flags, it may itself want to receive -Xmx and similar. My "--" separator proposal provides the power to do this. Another way would be e.g. Maven's "argLine" trick (http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#argLine). – ctrueden Mar 23 '16 at 15:57
  • So you're thinking all -- arguments get passed through as JVM args? Or to have @{-Xmx256M}? My proposal could cause conflicts, agreed. I could just hijack the few flags that the launcher understand. Actually that's probably how it'd be anyway because any argument the launcher understands it will also need to know how to potentially override it in the JVM user overrides. It gets complicated. The number of arguments will be limited either way. -Xmx, -Xdebug, what else would you like to see the launcher know about? – Chris Bensen Mar 24 '16 at 16:47
  • I should also mention it is preferred if arguments are consistent across the Java platform. I have recently added support for "javapackager -Xmx" in JDK9. It would be a bit odd to have it be different, but I also don't want to interfere with arguments applications expect. This is why we didn't do this for 8u40. – Chris Bensen Mar 24 '16 at 17:05
  • 1
    My proposal is that you could write e.g.: `myImageViewer -Xmx512m -Xdebug -- myFile.png` and the `--` separator would unambiguously divide the JVM arguments from the application-side arguments, with the JVM arguments coming first. This behavior is in the same spirit as *nix utilities which use `--` to divide between program flags and file arguments. I do also like your `@{...}` syntax though—that is closer to the Maven argLine approach of writing e.g. `myImageViewer -DargLine="-Xmx512m -Xdebug" myFile.png` and having the launcher pick up that system property value as extra JVM args. – ctrueden Mar 31 '16 at 20:59
  • In any case, I think the main consideration should be to make it as "future-proof" as possible, so that future changes to supported JVM arguments do not need to ripple into the JavaFX deployment code at all. In other words: it would be nice if any solution chosen is totally agnostic of the actual JVM flags. – ctrueden Mar 31 '16 at 21:02
0

Rather than having any kind of two-phase launch system, where you see what options you have and then launch a replacement JVM, I would suggest you create your own native launcher by copying and editing the platform java.c launcher. You can see what they are doing in the open JDK project.

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

There are quite a few places where they are looking for various options and converting them into init arguments for the JVM itself. Take a look at the functions ParseArguments, AddApplicationOptions, TranslateApplicationArgs, CheckJvmType, AddOption etc.

I am no C programmer but I have maintained my own launcher on quite a few occasions: such as a launcher that loaded a specific class path from a one-entry-per-line text file that could be checked into source control and one that used a different entry point to main(). It doesn't change that much and you can isolate your changes quite easily, so that they are easy to reapply on later versions of java.c. For your purposes, you would not really need to change every time someone makes a change to java.c, you only really need to change it when JavaVMInitArgs or some other critical aspect of the invocation interface changes.

I am sure that if you proposed and contributed a more elegant option-handling solution, maybe one that behaved differently when argv[0] is not 'java', then maybe the open jdk team would adopt your approach and maintain it for you, or for all us. I am sure there are lots of developers out there needing features like these.

  • 1
    Thanks, Juan Carlos. As I described in the question above, we do already have a native launcher actually, but it is too complex. I suppose we could start fresh with a new, very simple launcher, but ideally I'd like to avoid maintaining _any_ native code at all, which is what this question is really about. Surely the need for users to configure which JVM is launched, for example, is common across many Java applications? Good idea about submitting a patch to OpenJDK. Perhaps that is the best way to go here. – ctrueden Sep 30 '15 at 16:46