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.
The approach that no longer works for me
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:
java.lang.ClassNotFoundException: javafx.application.Platform
So the JVM wasn’t able to find and load the core JavaFX libraries, which are required by the ROES client.
Packages to install
I’m on Debian, so the packages I list are as named/packaged in Debian. YMMV. That said, these are the important ones:
-
icedtea-netx
provides the “Java Web Start” implementation that can interpret and run.jnlp
files, which is how ROES is executed. -
openjfx
provides the JavaFX implementation that will be used by the ROES client. -
openjdk-11-jre
the 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 theopenjfx
version, so I figured it had the highest chance of success.
Building a command line
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 main
class, net.sourceforge.jnlp.runtime.Boot
.
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 -nosecurity
after -Xnofork
.
The command line that worked for me
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)