Jpackage in Java: Native Installers
jpackage is a JDK tool that turns a Java application into a native package for an operating system: an application bundle or installer that a user opens like any other program. On macOS it produces a .app, .dmg or .pkg; on Windows an .exe or .msi; on Linux a .deb or .rpm. The package contains its own Java runtime (built with jlink behind the scenes), so users don't need to install Java or even know the program is written in it. jpackage became a standard tool in Java 16. In this lesson, you will learn what it produces, its main options, and package the expense tracker as a native application.
Jlink vs Jpackage
jlink | jpackage | |
|---|---|---|
| Produces | A runtime image: a plain folder | A native application or installer |
| Started with | A script in bin/ | A double-click, a Start menu entry, a desktop icon |
| Includes a runtime | Yes | Yes (it runs jlink for you) |
| Works with non-modular JARs | No | Yes |
| Adds icons, file associations, shortcuts | No | Yes |
Package Types
| Platform | --type values | Extra tools needed |
|---|---|---|
| Any | app-image (the application folder, no installer) | None |
| macOS | dmg, pkg | Xcode command-line tools |
| Windows | exe, msi | WiX Toolset |
| Linux | deb, rpm | dpkg-deb or rpmbuild |
jpackage can't cross-build: a Windows installer must be built on Windows, a macOS one on macOS.
Options
| Option | Description |
|---|---|
--type <type> | It is used to choose the kind of package. |
--name <name> | It sets the application's name. |
--app-version <version> | It sets the version shown to the user. |
--dest <dir> | It sets where the result is written. |
--module-path <path>, --module <module> | They select a modular application and its main module. |
--input <dir>, --main-jar <jar>, --main-class <class> | They select a non-modular application from a folder of JARs. |
--icon <file> | It sets the application icon. |
--java-options <options> | It passes options, such as -Xmx512m, to the JVM at each start. |
--runtime-image <dir> | It uses a runtime image you built with jlink. |
--vendor, --copyright, --description | They set descriptive information. |
Example: Packaging the Expense Tracker
Using the modular JARs from "JAR Files and Executable JARs in Java", on macOS:
jpackage --type app-image --name Expenses --app-version 1.0 \
--module-path mods --module expenses.app --dest dist
ls dist
ls dist/Expenses.app/Contents
dist/Expenses.app/Contents/MacOS/ExpensesExpenses.app
Info.plist
MacOS
PkgInfo
Resources
_CodeSignature
app
runtime
By category: {Food=649.75, Travel=1200}
Total: 1849.75
Running in module: expenses.appExplanation:
--type app-imageasks for the application itself without an installer around it. On macOS that is an.appbundle.--module-path mods --module expenses.appselects the modular application.jpackagerunsjlinkto build a runtime containing only the needed modules.- Inside the bundle,
MacOS/Expensesis a native launcher,runtimeis the trimmed Java runtime, andappholds the application's configuration. - Running the launcher starts the program with no JDK on the path. This bundle is 42 MB on the test machine, almost all of it the runtime.
On Windows or Linux the same command produces a folder with an Expenses.exe or an Expenses launcher in bin/.
A Non-Modular Application
Most existing applications are plain JARs. jpackage handles those too: put the application JAR and its dependency JARs in one folder and point at it.
jpackage --type app-image --name Hello --input lib \
--main-jar hello.jar --main-class com.school.Hello --dest distFor a non-modular application, jpackage includes a default set of JDK modules. To keep the package small, list only what you need with --add-modules, using the output of jdeps --print-module-deps.
From App Image to Installer
To make something users can download and install, change the type:
jpackage --type dmg --name Expenses --app-version 1.0 \
--module-path mods --module expenses.app --dest distOptions for installers add polish: --win-menu and --win-shortcut on Windows, --linux-shortcut on Linux, --mac-package-identifier on macOS, --license-file for a license screen, and --file-associations to open a file type with your application.
Signing
Modern operating systems warn about, or block, software from unknown publishers. For public distribution you must sign the package: with an Apple Developer ID and notarization on macOS (--mac-sign), or with a code-signing certificate on Windows. Signing isn't needed for internal use and testing.
Common Mistakes
Trying to build a Windows installer on a Mac. Not possible; build on each platform.
Missing packaging tools. msi needs WiX on Windows, rpm needs rpmbuild; jpackage reports what it can't find.
A huge package for a tiny program. Without --add-modules, a non-modular application gets a large default runtime.
Forgetting updates. The bundled runtime doesn't update itself; ship new installers when you move to a patched JDK.
Practice
- Easy: Package any executable JAR of yours with
--type app-imageand run the launcher it creates. - Harder: Add
--java-options "-Xmx256m"and an--icon, rebuild, and build an installer type for your platform.
Key Takeaways
Was this tutorial helpful?