How to package and distribute Java applications is a long lasting issue in Java platform. Users want to have a smooth experience when installing Java applications. For example, on Windows platform, they want to simply double-click a file to install. There was no practical built-in tools to package Java applications. So we have to either distribute JAR files or using third-party tools like install4j.
JEP 343 provides an incubating tool
jpackage to package self-contained Java applications. This JEP is part of JDK 14.
jpackage packages a Java application into a platform-specific package that includes all of the necessary dependencies. It supports different platform-specific package formats.
- Linux -
- macOS -
- Windows -
Get JDK 14
jpackage, you need to have JDK 14. JDK 14 is not released yet. You can get early access builds from jdk.java.net.
It's recommended to use SDKMAN! to install JDK.
$ sdk install java 14.ea.30-open
You don't need to set JDK 14 as the default JDK. You can set JDK 14 only for a terminal session.
$ sdk use java 14.ea.30-open
Below is the output of
java -version of JDK 14 used in this post.
openjdk version "14-ea" 2020-03-17 OpenJDK Runtime Environment (build 14-ea+30-1385) OpenJDK 64-Bit Server VM (build 14-ea+30-1385, mixed mode, sharing)
We use a Spring Boot sample application created from start.spring.io as the example. It's a Maven-based simple Spring MVC app. We can use Maven command to package the application as the single JAR file
To create a package for the sample application, we put the
lib directory and run
jpackage to package.
$ jpackage -n spring-demo -i lib --main-jar demo-0.0.1-SNAPSHOT.jar -d output
Note: The command above requires the JAR file to be executable, i.e. the
MANIFEST.MF file has the attribute
Main-Class. If the JAR file is not executable, you need to use
--main-class to provide the main class. The JAR file
demo-0.0.1-SNAPSHOT.jar created by Spring Boot Maven plugin is executable, so it can be used directly.
After running the command above, we can see the file
output directory. We can double-click this DMG file to install. Easy!
Custom Runtime Image
For modular applications, you can use
--module-path to provide application modules. The resulting package only contains modules required by the application.
$ jpackage --name myapp --module-path lib -m myapp
For non-modular applications,
jpackage will include a lot of JDK modules, even though some of these modules are not required. To build a smaller package, you can use
jlink to created a custom runtime image first, then use
--runtime-image option to specify the custom runtime image.
The first step is to find out what JDK modules are required by the application. This can be done using jdeps. However, we cannot use the JAR file created by Spring Boot Maven plugin, because the JAR file uses a special format to organize dependencies. You should use Maven Dependency Plugin or Maven Assembly Plugin to collect JAR files of all dependencies into a directory.
Suppose that all JAR files are put into
lib directory. We can use the following command to print out modules required by the application.
jdeps --class-path "lib/*" \ --multi-release base \ --ignore-missing-deps \ -recursive \ --print-module-deps \ lib/demo-0.0.1-SNAPSHOT.jar
The output for the sample Spring application is shown as below.
The output can be used by
The second step is to create custom runtime image using
jlink. In the command below, the value of
--add-modules option comes from output of
$ jlink --add-modules java.base,java.desktop,java.instrument,java.management.rmi,java.naming,java.prefs,java.scripting,java.security.jgss,java.sql,jdk.httpserver,jdk.unsupported --output spring-demo-runtime
After running the command above, the
spring-demo-runtime contains the created runtime image.
The last step is to create a package using the custom runtime image. The value of
--runtime-image comes from the custom runtime image created in the last step.
$ jpackage -n spring-demo -i lib \ --main-jar demo-0.0.1-SNAPSHOT.jar -d output \ --runtime-image spring-demo-runtime
If the application requires arguments when starting, you can use
--arguments to specify. The
--java-options option can pass options to JVM.
jpackage also supports other options to provide package metadata.
--app-version <version>: App version
--copyright <copyright string>: Copyright
--description <description string>: Description
--license-file <file path>: License file
--name/-n <name>: App name
--vendor <vendor string>: Vendor.