jpackage Tutorial: Packaging Java Apps (Java 16)
Introduction
Shipping a Java desktop or command-line application used to mean asking users to install a matching version of Java first, and then giving them a JAR file to run with
Step 2: Create an application image
The simplest output type is an application image (
Running the launcher runs the application on the bundled runtime, not on any Java installed on the machine -
OUTPUT of the above command
The bundled runtime is taken from the JDK that runs jpackage - here a Java 24 JDK - so build your packages with the Java version you want to ship.
Step 3: Make it smaller with a custom runtime
By default, jpackage bundles a large runtime with most of the JDK's modules, so even a "Hello" application is big -
Size of dist\HelloApp
Thanks to the module systemClick to read: module system, you can bundle only the modules the application needs.
OUTPUT of jdeps
Size of dist\HelloSmall
Our application uses only
Other useful options
java -jar. Since Java Web Start and the browser plugin were removed in Java 11Click to read: Java 11, there was no standard way to install Java applications at all. jpackage, incubated in Java 14 and made a standard tool in Java 16Click to read tutorial: Overview of New Features in Java 16 (JEP 392), fills this gap: it packages an application together with its own Java runtime, either as a self-contained folder or as a native installer - .msi or .exe on Windows, .pkg or .dmg on macOS, .deb or .rpm on Linux. This jpackage tutorial goes through it step by step: building an application image, making it smaller with a custom runtime, creating installers, and the most useful options.
Step 1: The application
jpackage works with an ordinary application in a JAR file. Our example prints which Java it is running on -
The application to package
//HelloApp.java
import java.nio.file.Path;
public class HelloApp {
public static void main(String[] args) {
Path javaHome = Path.of(System.getProperty("java.home"));
System.out.println("Hello from a packaged app!");
System.out.println("Running on Java " + Runtime.version().feature()
+ " from the folder: " + javaHome.getParent().getFileName() + "\\" + javaHome.getFileName());
}
}
javac -d classes HelloApp.java jar --create --file lib/app.jar --main-class HelloApp -C classes .
--type app-image): a folder containing a native launcher, the application's JARs, and a Java runtime. It needs no extra tools, so it is the best way to start -
jpackage --type app-image --name HelloApp --input lib --main-jar app.jar --win-console --dest dist
--input libis the folder with the application's JAR files - all of them are copied into the package - and--main-jarnames the JAR whose manifest gives the main class. For a modular application, use--module-pathand--moduleinstead.--win-consolemakes the Windows launcher a console program, so its output appears in the terminal. Without it, the launcher is a GUI program, which suits Swing and JavaFX applications.--destis where the output is written.
app folder with the JAR, and a runtime folder with a complete Java runtime -
Contents of dist\HelloAppapp HelloApp.exe runtime
dist\HelloApp\HelloApp.exe
Hello from a packaged app! Running on Java 24 from the folder: HelloApp\runtime
152 MB
jdeps lists them, jlink builds a runtime containing just those modules, and --runtime-image tells jpackage to use it -
jdeps --print-module-deps lib/app.jar jlink --add-modules java.base --strip-debug --no-header-files --no-man-pages --output small-runtime jpackage --type app-image --name HelloSmall --input lib --main-jar app.jar --win-console --runtime-image small-runtime --dest dist
java.base
43 MB
java.base, so the package shrinks to a fraction of its size. A real application usually needs a few more modules, such as java.desktop for Swing or java.sql for JDBC - jdeps tells you which. If the application loads classes by reflection, check that the modules they come from are included too.
Step 4: Create a native installer
To produce an installer, change --type. The installer types depend on the operating system, and jpackage can only build installers for the system it runs on - so build Windows installers on Windows, and so on -
jpackage --type msi --name HelloApp --input lib --main-jar app.jar --win-menu --win-shortcut --dest installers
- Windows -
msiorexe. These need the free WiX Toolset to be installed. Options such as--win-menu,--win-shortcutand--win-dir-chooseradd a Start menu entry, a desktop shortcut and a choice of installation folder. - macOS -
pkgordmg. Applications distributed outside the App Store should be signed (--mac-sign) and notarized. - Linux -
deborrpm, which need thedpkgorrpmtools.
--app-version,--vendor,--description,--copyright- metadata shown by the installer and the operating system.--icon- the application's icon (.icoon Windows,.icnson macOS,.pngon Linux).--java-options- JVM options which the launcher always passes, for example--java-options "-Xmx512m".--arguments- default arguments to the main method.--file-associations- register the application to open certain file types.@options.txt- read the options from a file, which keeps long jpackage commands manageable in a build script.
jdeps and jlink to build a custom runtime containing only the needed modules, which made the package much smaller. Lastly, we looked at creating native installers for each operating system, and at the most useful options. Next, read about the strong encapsulation of JDK internalsClick to read tutorial: Strong Encapsulation of JDK Internals in Java 16 and 17.Continue Reading Java 12 to 17 Tutorials
Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Java 16 Records Tutorial with ExamplesClick to read tutorial: Java 16 Records Tutorial with Examples Java 16 Pattern Matching for instanceofClick to read tutorial: Java 16 Pattern Matching for instanceof Java 16 API Additions: Stream.toList, mapMulti and MoreClick to read tutorial: Java 16 API Additions: Stream.toList, mapMulti and More Strong Encapsulation of JDK Internals in Java 16 and 17Click to read tutorial: Strong Encapsulation of JDK Internals in Java 16 and 17 Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15
Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Java 16 Records Tutorial with ExamplesClick to read tutorial: Java 16 Records Tutorial with Examples Java 16 Pattern Matching for instanceofClick to read tutorial: Java 16 Pattern Matching for instanceof Java 16 API Additions: Stream.toList, mapMulti and MoreClick to read tutorial: Java 16 API Additions: Stream.toList, mapMulti and More Strong Encapsulation of JDK Internals in Java 16 and 17Click to read tutorial: Strong Encapsulation of JDK Internals in Java 16 and 17 Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15