Jpackage in Java: Native Installers

Naveen AnamaneniUpdated · 5 min read

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.

jlinkjpackage
ProducesA runtime image: a plain folderA native application or installer
Started withA script in bin/A double-click, a Start menu entry, a desktop icon
Includes a runtimeYesYes (it runs jlink for you)
Works with non-modular JARsNoYes
Adds icons, file associations, shortcutsNoYes

Package Types

Platform--type valuesExtra tools needed
Anyapp-image (the application folder, no installer)None
macOSdmg, pkgXcode command-line tools
Windowsexe, msiWiX Toolset
Linuxdeb, rpmdpkg-deb or rpmbuild

jpackage can't cross-build: a Windows installer must be built on Windows, a macOS one on macOS.

Options

OptionDescription
--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, --descriptionThey set descriptive information.

Example: Packaging the Expense Tracker

Using the modular JARs from "JAR Files and Executable JARs in Java", on macOS:

Terminal
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/Expenses
Terminal output
Expenses.app
 
Info.plist
MacOS
PkgInfo
Resources
_CodeSignature
app
runtime
 
By category: {Food=649.75, Travel=1200}
Total: 1849.75
Running in module: expenses.app

Explanation:

  1. --type app-image asks for the application itself without an installer around it. On macOS that is an .app bundle.
  2. --module-path mods --module expenses.app selects the modular application. jpackage runs jlink to build a runtime containing only the needed modules.
  3. Inside the bundle, MacOS/Expenses is a native launcher, runtime is the trimmed Java runtime, and app holds the application's configuration.
  4. 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.

Terminal
jpackage --type app-image --name Hello --input lib \
    --main-jar hello.jar --main-class com.school.Hello --dest dist

For 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:

Terminal
jpackage --type dmg --name Expenses --app-version 1.0 \
    --module-path mods --module expenses.app --dest dist

Options 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

  1. Easy: Package any executable JAR of yours with --type app-image and run the launcher it creates.
  2. Harder: Add --java-options "-Xmx256m" and an --icon, rebuild, and build an installer type for your platform.

Quick check: jpackage

4 questions
1What does jpackage create?
2Does the user need Java installed?
3Can a Windows .msi be built on macOS?
4Which works with non-modular JARs?

Key Takeaways

Was this tutorial helpful?

Lesson 519 of 578