0

I have searched for hours trying to find what I'm looking for but there seems to be very little information regarding the latest version of JavaFX.

For a project, I am required to deliver a standalone executable JAR file that can run on any computer with java installed. I am using JDK 14 and JavaFX 14 and I am not using Maven or Gradle (although if one of those is necessary, there's no reason I can't use them).

My project runs fine in IntelliJ with VM options pointing to my JavaFX lib folder but I can't find how to properly build a JAR that can be distributed an executed without having javaFX the other system. i.e. I would like JavaFX, my FXML files, as well as my other dependencies (which includes JDBC) to be bundled within the JAR. Is this possible?

Please tell me if I need to supply any more information. Thanks guys.

James Batchelor
  • 99
  • 1
  • 1
  • 14
  • 1
    It is not possible. JavaFX requires native libraries and they can’t be in your .jar file. You can, however, build an executable [image tree](https://stackoverflow.com/questions/53453212/how-to-deploy-a-javafx-11-desktop-application-with-a-jre) and distribute that. – VGR Apr 02 '20 at 15:26
  • @VGR what if I set up my project to use maven or gradle instead? would it be possible then? I must have an executable jar file. – James Batchelor Apr 02 '20 at 16:34
  • It is possible but difficult and IMHO does not make any sense. The way to go with modern JavaFX is to build an installer, so that you can install your software in the same way as you would install any other software on a given platform. As you are already using JDK 14 and JavaFX 14 you actually have already everything you need. The key tool to look for is jpackage, which comes bundled with JDK 14. – mipa Apr 02 '20 at 16:53
  • If you need an example of packaging have a look here: https://github.com/dlemmermann/JPackageScriptFX – mipa Apr 02 '20 at 16:55
  • thanks @mipa I understand that for distribution in any other scenario that would be exactly what I need. Unfortunately, I'm doing a group project for uni and having that JAR is non-negotiable. Since it was my decision to use JFX, I need to find a way of building this so everyone doesn't hate me... – James Batchelor Apr 02 '20 at 17:15
  • Using Maven or Gradle won’t make a difference. They make .jar files the same way all other tools do. – VGR Apr 02 '20 at 17:16
  • See [this answer](https://stackoverflow.com/a/52654791/6395627), [this answer](https://stackoverflow.com/a/55303408/6395627), and [this answer](https://stackoverflow.com/a/52571719/6395627) (this one claims fat jars are not possible at first, but the edit shows a workaround). Note that the fat jar requires the JavaFX jars from Maven Central as those embed the native code, unlike the jars from the SDK. – Slaw Apr 02 '20 at 17:25
  • 1
    @VGR "_JavaFX requires native libraries and they can’t be in your .jar file_" – the jars from Maven Central embed the native code and extract it at runtime. And [this answer](https://stackoverflow.com/questions/52653836/maven-shade-javafx-runtime-components-are-missing/52654791#52654791) shows a way to create a cross-platform fat jar. – Slaw Apr 02 '20 at 17:29
  • @JamesBatchelor Can you guarantee that everybody will have a Java version >= 11 installed? Otherwise you would not be able at all to satisfy your requirement. – mipa Apr 02 '20 at 17:56
  • @mipa yes, it can be assumed that the java version will be 11 or greater – James Batchelor Apr 02 '20 at 18:40

1 Answers1

1

I have found a Frankenstein solution!

IntelliJ can add external libraries to a regular Java project from the Maven repository without actually converting it to a Maven project (in fact, I was already using this for JUnit) I added the JavaFX dependencies as follows:

First I removed my existing dependencies and removed my VM options (these will no longer be required)

Project Settings -> Libraries -> + -> From Maven

and added:

  • org.openjfx:javafx-base:14
  • org.openjfx:javafx-controls:14
  • org.openjfx:javafx-fxml:14
  • org.openjfx:javafx-graphics:14

I had to click 'Apply' between each one or it wouldn't download them

I then created a JAR the normal way in IntelliJ:

Project Settings -> Artefacts -> JAR -> From modules with dependencies

added all the available elements to the output root

Build -> build artefacts

Upon running this, I got an error telling me it was looking for an FXML file so I copied my FXMLs to the same location and to my surprise, it ran flawlessly! I now plan to create a class within my project to generate the FXML files before my JavaFx code runs, that way, I can still distribute a single JAR.


Edit (Thanks @Slaw for your help)

Upon analysing the JAR, I found that my FXML files were indeed bundled inside. Accessing these is actually fairly simple, just change any FXMLLoader setLocation calls to:

myFxmlLoader.setlocation(MyClass.class.getResource("myFXMLFile.fxml"));

And it works flawlessly!

Thank you to everyone else for your suggestions too!

James Batchelor
  • 99
  • 1
  • 1
  • 14
  • I'm glad you're finding a solution, but "_I now plan to create a class within my project to generate the FXML files before my JavaFx code runs_" indicates a problem. Generating your FXML files at runtime is almost certainly not what you want to do (there would no longer be a point in using FXML as you could just code the UI instead). Your FXML files should be resources which are embedded in the JAR file and retrieved via APIs such as `Class#getResource(String)`. – Slaw Apr 03 '20 at 09:14
  • @Slaw Honestly, I was just happy to have it working last night and perhaps jumped the gun on my answer. Is there any way of bundling the FXML files into the JAR and passing that information to the FXMLLoader? (preferably only when the project is in JAR form so it can still run from my IDE) – James Batchelor Apr 03 '20 at 13:06
  • Resources are independent of how the application is packaged (or not packaged) as they're resolved on the classpath/modulepath. This is not specific to FXML or even JavaFX, but instead applies to all Java applications. For FXML files, it'd look something like `new FXMLLoader(getClass().getResource(/* resource name */))`. To know what to use as the resource name check out the documentation of [`Class#getResource(String)`](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Class.html#getResource(java.lang.String)). – Slaw Apr 03 '20 at 13:40