After using the ROES printing client (which I use with BayPhoto and WHCC) for a really long time, I discovered earlier this year that recent versions use JavaFX, and I could no longer launch it with just
javaws roes_client.jlnp. The following isn’t a completely finished solution, but it got me to the point of sending out a new print order, so I figured that’s good enough to write it up.
If I just run
javaws roes_client.jlnp, I click through the normal dialogs before getting an error like the following:
netx: Launch Error: Could not launch JNLP file. ( (javafx/application/Platform (javafx.application.Platform))) net.sourceforge.jnlp.LaunchException: Fatal: Launch Error: Could not launch JNLP file. The application has not been initialized, for more information execute javaws/browser from the command line and send a bug report. at java.desktop/net.sourceforge.jnlp.Launcher.launchApplication(Launcher.java:583) at java.desktop/net.sourceforge.jnlp.Launcher$TgThread.run(Launcher.java:946) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.desktop/net.sourceforge.jnlp.Launcher.launchApplication(Launcher.java:577) ... 1 more Caused by: java.lang.NoClassDefFoundError: javafx/application/Platform at com.softworks.roes.app.ROESApp.<init>(Unknown Source) at com.softworks.roes.app.ROESApp.main(Unknown Source) ... 6 more Caused by: java.lang.ClassNotFoundException: javafx.application.Platform at java.desktop/net.sourceforge.jnlp.runtime.JNLPClassLoader.loadClass(JNLPClassLoader.java:1645) ... 8 more
The error all the way at the bottom is the root cause:
So the JVM wasn’t able to find and load the core JavaFX libraries, which are required by the ROES client.
I’m on Debian, so the packages I list are as named/packaged in Debian. YMMV. That said, these are the important ones:
icedtea-netxprovides the “Java Web Start” implementation that can interpret and run
.jnlpfiles, which is how ROES is executed.
openjfxprovides the JavaFX implementation that will be used by the ROES client.
openjdk-11-jrethe base Java runtime environment that will be used by the ROES client. I haven’t tried this with more recent versions — I picked 11 because it matches the
openjfxversion, so I figured it had the highest chance of success.
javaws is actually just a shell script that runs
java, so we can use
bash with command echoing to see what the base
java command is:
$bash -x $(which javaws) launch.jnlp […] + exec -a javaws /usr/lib/jvm/default-java/bin/java -splash:/usr/share/icedtea-web/javaws_splash.png -Xbootclasspath/a:/usr/share/icedtea-web/javaws.jar:/usr/share/java/js.jar:/usr/share/java/tagsoup.jar:/usr/lib/jvm/default-java/lib/ext/nashorn.jar -Xms8m --patch-module java.desktop=/usr/share/icedtea-web/javaws.jar: @/usr/share/icedtea-web/bin/itw-modularjdk.args -classpath /usr/lib/jvm/default-java/lib/rt.jar:/usr/lib/jvm/default-java/lib/ext/jfxrt.jar -Dicedtea-web.bin.name=javaws -Dicedtea-web.bin.location=/usr/share/icedtea-web/bin/javaws.sh -Djava.security.manager -Djava.security.policy=/etc/icedtea-web/javaws.policy net.sourceforge.jnlp.runtime.Boot launch.jnlp
And we know that the JVM will look for classes to load on the classpath. Two important
javaws arguments are the following:
$javaws --help […] -nosecurity - Disables the secure runtime environment. You need also deployment.security.itw.ignorecertissues to workaround corrupted signatures(No argument expected) […] -Xnofork - Do not create another JVM.(No argument expected)
On Debian, the
openjfx JARs are installed at
/usr/share/openjfx/lib/*.jar. Because I don’t know a simple way to join the OpenJFX JAR filenames with a colon character in bash, I just did it in ruby, using
ruby -e 'puts ARGV.join(":")' /usr/share/openjfx/lib/*.jar
And because I’m going to modify the classpath by hand, I need to make sure that the
jnlp loader doesn’t start a new sub-JVM (which may not use the same classpath), so I use
-Xnofork as the first option after the
Lastly, I would need to create a new security manager config that provides permissions to OpenJFX (since it’s not part of the JDK/JRE), but I just disabled the security manager instead, by removing
-Djava.security.manager -Djava.security.policy=/etc/icedtea-web/javaws.policy from the command line, and adding
All that said, here’s the command line that finally worked. Instead if figuring out how to get
javaws to do what I needed, I just ran the
java command by hand, based on what it had originally run. Note that I’m using the outer set of parens to create a sub-shell, so that the
exec command doesn’t terminate my terminal shell — I don’t know how important it is for the java process to be called
javaws, but I figured it’s easy enough to keep that working, so I did it.
$ (exec -a javaws /usr/lib/jvm/default-java/bin/java -splash:/usr/share/icedtea-web/javaws_splash.png -Xbootclasspath/a:/usr/share/icedtea-web/javaws.jar:/usr/share/java/js.jar:/usr/share/java/tagsoup.jar:/usr/lib/jvm/default-java/lib/ext/nashorn.jar -Xms8m --patch-module java.desktop=/usr/share/icedtea-web/javaws.jar: @/usr/share/icedtea-web/bin/itw-modularjdk.args -classpath /usr/lib/jvm/default-java/lib/rt.jar:/usr/lib/jvm/default-java/lib/ext/jfxrt.jar:$(ruby -e 'puts ARGV.join(":")' /usr/share/openjfx/lib/*.jar) -Dicedtea-web.bin.name=javaws -Dicedtea-web.bin.location=/usr/share/icedtea-web/bin/javaws.sh net.sourceforge.jnlp.runtime.Boot -Xnofork -nosecurity launch.jnlp)