n41r0j
n41r0j

Reputation: 143

Bundle JavaFX app with openjdk 11 + runtime

I've created a small HelloWorld Java app that relies on OpenJDK 11 and JavaFX. The app is packaged in a jar file which can only be run if I have installed Java 11 and JavaFX on my system separately.

Now I'd like to convert my jar into a self-contained Java application which includes JavaFX and a fully-functional Java runtime environment. This will allow me to run my app without installing OpenJDK 11 (which presents technical hurdles such as setting PATH correctly etc).

I can find information about creating self-contained Java applications on Java 10 but I cannot find information about bundling a Java app with OpenJDK 11 and JavaFX.

How can I ship a self-contained Java app (which includes a java runtime) with OpenJDK 11 and JavaFX?

Upvotes: 13

Views: 5964

Answers (5)

Basil Bourque
Basil Bourque

Reputation: 340070

Modular Java, jlink, & jpackage

Follow these tutorials found at the new home for JavaFX after having been spun out of Oracle to Gluon.

You will need to write code using modular Java. Add JavaFX 11 as a dependency to your project. And use the new linking/packaging tools to bundle a subset of the JDK within your app.

Learn about:

No more JRE

Oracle no longer intends for end-users to be installing a JRE or a JDK. Java Applets in a browser and Java Web Start app delivery are both being phased out, leaving the end-user with no need for a JRE. Java-based apps are expected to bundle their own Java implementation. The only folks consciously installing a JDK will be developers & server-side sysadmins.

Important:

  • Understand clearly the nature of the OpenJDK project, as explained in Wikipedia.
  • Know that Oracle has committed to feature-parity between its own branded Oracle JDK and the OpenJDK project. This commitment includes donations of previously-commercial features such as Flight Recorder and Mission Control.
  • OpenJFX, the open-source development of JavaFX, is a part of the OpenJDK project. OpenJFX may or may not be included in a build of OpenJDK. The Java specifications do not require JavaFX functionality.
  • At least two vendors provide an edition of their JDK product that comes bundled with the OpenJFX libraries:
    • ZuluFX from Azul Systems
    • LibericaFX from BellSoft
  • Read this white paper by Oracle of 2018-03, Java Client Roadmap Update
  • Read the white paper Java Is Still Free, authored by key members of the Java community.

Here is a flowchart diagram that may help you finding and deciding amongst the various vendors providing a Java 11 implementation.

Flowchart guiding you in choosing a vendor for a Java 11 implementation


Motivations in choosing a vendor for Java

Upvotes: 16

Marinos An
Marinos An

Reputation: 10878

(Using jdk14)

Starting by the fact that in order to use jlink your main jar should be a module.

How? Consider that you have a maven project. You just need to include module-info.java inside src/main/java dir and make sure that you require the modules that your app needs and you export the package that contains your main class. In most cases you will get a compile-time error when missing a requires. Have in mind that non-modular dependencies become automatic modules.

You can use maven's copy-dependencies to make sure that all dependencies are copied under target/lib during mvn package.

next step: jlink

Since jlink maven plugin is still in alpha, you can use command-line.

NOTES:

  • jlink will create a self-contained bundle directory that contains
    • main app module
    • app dependencies
    • jdk required modules
    • app launcher (optional)
  • jlink bundle targets one platform at a time. By default it is the current platform.
  • javafx runtime modules are also platform-specific. But since they are not part of the jdk we need to always provide the module-path containing them.
  • javafx runtime modules can be downloaded from web, or from maven repo by using the corresponding target platform classifier (win/linux/mac).
  • jlink can also create cross-platform bundles. Just include the target platform modules to the --module-path (e.g. from linux: download windows jdk/ javafx and add their jmods dirs to module-path).

jlink command

Case 1: build and target platforms are the same

NOTE: /path-to/javafx-mods needs to be provided to your modulepath unless you copy the required javafx deps under lib/ using maven (copy-dependencies).


jlink --launcher run=jdk14Example/com.example.javafx.app.Main \
--module-path ./lib:javafx-jdk14-example-1.0.0.jar:/path-to/javafx-mods \
--add-modules=jdk14Example --output app-bundle

Case 2: build and target platforms are differrent

# Building from linux for windows
jlink --launcher run=jdk14Example/com.example.javafx.app.Main  \
--module-path ./lib:javafx-jdk14-example-1.0.0.jar:/path-to/jdk-win/jmods:/path-to/javafx-mods-win \
--add-modules=jdk14Example --output app-bundle

Conclusion:

In both of the above cases you get a directory with a self-contained application which can run on a workstation with no java/javafx installed.

# if jlink targeted linux
app-bundle/bin/run

# if jlink targeted windows
app-bundle/bin/run.bat

# if jlink targeted mac
app-bundle/bin/run

Upvotes: 1

tresf
tresf

Reputation: 7922

Native Libraries

A challenge I encountered was to inform JavaFX about it's own native libraries (.dll, .dylib, .so, etc). Fortunately, getting the dylibs loaded is as simple as setting the java.library.path using System.setProperty(...).

Historically, setting this variable is argued/perceived as pointless in Java as it's too late for the classloader (inferior to -Djava.library.path) and forcing it using reflection is a forbidden security violation since Java 10... fortunately, JavaFX actually honors this variable naturally without any violations or hacks and will pick it up after it's set.

// Detect the path to the currently running jar
String jarPath = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();

// Fix characters that get URL encoded when calling getPath()
jarPath = URLDecoder.decode(jarPath, "UTF-8");

String parentFolder = new File(jarPath).getParent();

// If libglass.dylib is next to the jar in a folder called "/bin"
System.setProperty("java.library.path",  parentFolder + "/bin");

// ... then make any javafx calls

Java Libraries

Naturally, the .jar files need to be accessible too. I do this the same as I would any java bundle by zipping them into the distribution (making a single, large .jar file)

These .jar files should be consistent with all JavaFX 11 distributions and should be bundled accordingly.

javafx-swt.jar
javafx.base.jar
javafx.controls.jar
javafx.fxml.jar
javafx.graphics.jar
javafx.media.jar
javafx.swing.jar
javafx.web.jar

Java 8 Compatibility

Initial tests against Java 8 using the above technique are positive. For now, I'm using Java version detection (not included in the above example) and ONLY setting java.library.path for Java 11 or higher. Java 8 is EOL for personal use Dec 2019 (EOL for commercial use Jan 2019) so it is important to offer compatibility as clients migrate from one LTS release to another.

Upvotes: 1

Gnas
Gnas

Reputation: 718

You can bundle a whole JDK with your app and create a batch script to run your app using the bundled JDK. I know this approach will bloat up your release significantly, but the alternative is to ask your user to install JDK themselves, which is not trivial for non-tech savvy people. Or you can release both versions, one with JDK bundled and one without.

Upvotes: 2

mipa
mipa

Reputation: 10640

Maybe you just wait a little bit until the first EA release of the new jpackager tool is available. See http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-October/056186.html

Upvotes: 1

Related Questions