Deploying non-modular JavaFX applications using Maven and launch4j
Have you always struggled with deploying JavaFX applications? If so, this article is for you.
What is JavaFX?
JavaFX is a software platform for creating and delivering desktop applications, as well as rich web applications that can run across a wide variety of devices. JavaFX has support for desktop computers and web browsers on Microsoft Windows, Linux, and macOS, as well as mobile devices running iOS and Android
Getting started
This is my first medium article, so any comments on how to improve would be greatly appreciated! 😊
Since JavaFX was removed by oracle from JDK11 it is now maintained as an open source project. Furthermore, it now follows a modular approach which makes it difficult for a beginner to run and deploy even a simple project.
Create a maven project in your favorite IDE. I will be using IntelliJ, a.k.a best IDE for java 😏 , for this tutorial. Also, this tutorial works assuming that you have a JDK version ≥ 11.
Copy the following to your pom.xml file. These are the JavaFX dependencies which our application will utilize. You can also change the version of the dependencies. If you are using any external dependencies, even they can be listed here.
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>11</version>
</dependency>
</dependencies>
Now create a Main class and copy the following contents to your java file. This file includes a simple class that extends the Application interface of the JavaFX library allowing us to create a simple window and inject content inside it.
package main.java;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception {
String javaVersion = System.getProperty("java.version");
String javafxVersion = System.getProperty("javafx.version");
Label label = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");
Scene scene = new Scene(label, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Now if you try to execute the main method inside this class, you will be shown an error that might confuse you.
Relax. This error is common and usually you would have to download the JavaFX SDK to your system, add its path as an environmental variable and add extra VM options to your IDE to solve this. Phew! 😨
But, let’s take a less scary approach!
Since JavaFX was removed from Oracle JDK after JDK11, it has taken a modular approach which makes things a little trickier if you do not understand the Java module system properly.
Now, to solve this error, let’s create another class called Launcher and add the following contents to it.
package main.java;
public class Launcher {
public static void main(String[] args) {
Main.main(args);
}
}
Easy. Right? What we are doing here is executing the main method of our Main class through another class. This will trick the LauncherHelper into thinking that we are running a normal application. Now, try to execute the main method in the Launcher class and see if everything works properly.
Time to add some maven plugins!
This snippet contains the following plugins with some additional configurations.
- maven-compiler-plugin
- maven-jar-plugin
- maven-assembly-plugin
Be sure to change the name inside the <mainClass> tag if you have used some other name for your launcher class.
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>Launcher</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>main.java.Launcher</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Remember to load maven changes once you have changed the maven file.
Let’s create the jar file
If you are not using IntelliJ, you can use the following command
mvn clean package
This will give the same end result as the above method. In your target folder you should now have two jar files. Your jar file will look like this
<project_name>-1.0-SNAPSHOT-jar-with-dependencies.jar
You can delete the other jar file. Try to execute this jar file using the command
java -jar <jar_file_name>.jar
Now that you have the jar file, you are 50% done! But, there is a problem. You cannot give your client the jar file and ask him to execute it. Usually applications have an executable that are easy to use in terms of end user’s perspective.
launch4j to the rescue!
What is launch4j?🤔
Launch4j is a cross-platform tool for wrapping Java applications distributed as jars in lightweight Windows native executables. The executable can be configured to search for a certain JRE version or use a bundled one, and it’s possible to set runtime options, like the initial/max heap size.
Download launch4j from here
Once you install it, open it by double clicking on the exe file. The launch4j interface looks like this.
Start by filling the details
- Output file: The name of your exe file.
- Jar: The path to your jar file.
There are a lot of other options which I will let you explore yourself. Some of the features are
- Adding icon
- Allowing only one instance of the app to be opened at a time
- Embedding JRE to your application
- Adding an admin manifest etc.
But before we move forward there is one more field which we need to fill.
Before building an executable from the jar file, launch4j requires you to enter the minimum JRE version which is required by your application to execute properly.
This is very helpful because in case your client’s system does not java, this automatically gives a popup pointing to the appropriate site.
Once you have entered the minimum JRE version, you are ready to create the executable file.
Click on the gear icon and the following window will be opened.
Name the file config and save it. launch4j requires this file to parse all the inputs that you have entered in their GUI. Once you save the file, your exe file will be created in the entered location.
Further deployment
Once you have created the executable, it is possible to create an installer for the same using applications like innosetup compiler or install4j.
I hope this got you on the right track towards JavaFX deployment. Let me know in the comments if you would like an article on creating an installer and an executable with embedded JRE or deployment to other platforms! That’s all folks! Thank you.
Follow me on