If you’re a Java developer, you may have faced challenges in delivering your applications to end users, especially when it comes to ensuring compatibility with their systems. You might have also faced the challenge of bundling your application with a Java runtime, or making sure that the users have the right version of Java installed on their machines. This can be a time-consuming process that detracts from the overall user experience.
Fortunately, Java 14 introduced JPackage, a powerful new tool that simplifies these challenges by allowing you to create native installers and packages for your Java applications. In this article, we’ll dive into what JPackage is, why it’s beneficial, and how you can start using it today.
What is JPackage?
JPackage is a command-line utility designed to package Java applications into native platform installers. By taking a Java application and a Java runtime image as inputs, JPackage produces an application image with all dependencies bundled in, streamlining the installation process for users. This tool can generate platform-specific installers, such as an exe for Windows or a dmg for macOS, so users can easily install your application without manual setup. However, note that each package format must be built on the respective platform due to a lack of cross-platform packaging support.
Why Use JPackage?
It is standard practice while distributing software to deliver an installable package to the end user. This package is compatible with the user's native platform and hides the internal dependencies and setup configurations. Delivering software in a native package format simplifies installation and enhances user experience by making your application feel like a natural part of the user's operating system.
JPackage allows you to create native packages for your JAR files, which eliminates the need for users to manually copy files or install Java themselves. Here’s why JPackage is beneficial:
- Platform-Specific Packaging: Distribute your software in widely recognized formats, such as DMG for macOS, EXE or MSI for Windows, and RPM or DEB for Linux.
- Improved User Experience: With JPackage, users can install, update, and uninstall your application just like any other native software, without needing a separate Java installation.
- Simplified Distribution: JPackage includes the Java runtime with your application, so compatibility issues are minimized, ensuring users get a seamless experience across different operating systems.
Prerequisites
To get started with JPackage, ensure your system meets the following prerequisites:
- Java Application: The system used for packaging must contain the application to be packaged (a pre-built JAR file of your application).
- Packaging Tools: The underlying packaging tools must be installed on your
system for JPackage to function properly:
- On Windows, we need to install WiX Toolset (3.0 or later) to create EXE or MSI files.
- On Linux, we need the rpm-build package for creating RPM files (Red Hat) and fakeroot package for creating DEB files (Ubuntu/Debian).
- On macOS, we need to install Xcode Command Line Tools for signing and customizing the DMG image.
Each package type must be created on its respective platform. To distribute your application for multiple operating systems, run the JPackage command on each platform to create the appropriate installers. In this article. we'll create an exe installer for Windows.
Creating an EXE Installer on Windows
To package a Java application into an EXE installer, let’s use a simple batch script example that works for applications built with Maven. As mentioned in the above section, the application JAR should be pre-built, and it will be used as an input to the JPackage tool. Execute the script from the root of your Java project to generate an EXE installer package for Windows:
@echo off
set name=[App Name]
set vendor=[App Publisher]
set year=[Copyright Year]
set version=[App Version]
set input_dir=target
set working_dir=target
set resources_dir=resources
set output_dir=release
set seven_zip="C:\Program Files\7-Zip\7z.exe"
set nospace=%name: =-%
if exist "%working_dir%\app" rd "%working_dir%\app" /s /q
if exist "%working_dir%\temp" rd "%working_dir%\temp" /s /q
if exist "%output_dir%\%nospace%-%version%*.*" del "%output_dir%\%nospace%-%version%*.*"
md "%working_dir%\app\lib"
md "%working_dir%\temp"
copy "LICENSE" "%working_dir%\app"
copy "%input_dir%\*.jar" "%working_dir%\app"
copy "%input_dir%\lib" "%working_dir%\app\lib"
jpackage^
--input "%working_dir%/app"^
--dest "%output_dir%"^
--resource-dir "%resources_dir%"^
--temp "%working_dir%/temp"^
--name "%name%"^
--vendor "%vendor%"^
--main-jar "%name%.jar"^
--app-version %version%^
--copyright "Copyright (c) %year% %vendor%"^
--icon "%resources_dir%/app.ico"^
--add-modules java.base,java.desktop,java.sql^
--jlink-options "--no-header-files --no-man-pages --strip-debug --strip-native-commands --compress=2"^
--type exe^
--license-file "%resources_dir%/LICENSE.rtf"^
--win-dir-chooser^
--win-menu^
--win-menu-group "%vendor%"^
--win-shortcut^
--win-shortcut-prompt
ren "%output_dir%\%name%-%version%.exe" "%nospace%-%version%-win-x64.exe"
copy "%working_dir%\temp\images\win-exe.image\%name%-%version%.msi" "%output_dir%\%nospace%-%version%-win-x64.msi"
del "%working_dir%\temp\images\win-msi.image\%name%\app\.jpackage.xml"
%seven_zip% a "%output_dir%\%nospace%-%version%-win-x64.zip" ".\%working_dir%\temp\images\win-msi.image\%name%\*"
del "%working_dir%\temp\images\win-msi.image\%name%\app\%name%.cfg"
%seven_zip% a "%output_dir%\%nospace%-%version%.zip" ".\%working_dir%\temp\images\win-msi.image\%name%\app\*"
rd "%working_dir%\app" /s /q
rd "%working_dir%\temp" /s /q
pause
Here’s a detailed breakdown of what this batch script does:
1. Setting Variables
- name, vendor, year, and version: These variables represent the application's metadata, like the application name, publisher, copyright year, and version. Update these to match the details of your application.
- input_dir, working_dir, resources_dir, and output_dir: These directories are where the application’s resources (like JAR files and icons) are stored, the working directory for JPackage, and the output directory for the final EXE package.
- seven_zip: Points to the path of the 7-Zip executable, which compresses files for distribution.
2. Removing Existing Temporary Directories and Files
if exist "%working_dir%\app" rd "%working_dir%\app" /s /q
if exist "%working_dir%\temp" rd "%working_dir%\temp" /s /q
if exist "%output_dir%\%nospace%-%version%*.*" del "%output_dir%\%nospace%-%version%*.*"
These lines clean up any previous temporary files and directories from earlier builds to avoid conflicts. /s /q removes directories silently and includes all files within subdirectories.
3. Creating Required Directories
md "%working_dir%\app\lib"
md "%working_dir%\temp"
These md commands create the necessary folders for storing application libraries and temporary files generated during packaging.
4. Copying Necessary Files
copy "LICENSE" "%working_dir%\app"
copy "%input_dir%\*.jar" "%working_dir%\app"
copy "%input_dir%\lib" "%working_dir%\app\lib"
These copy commands move essential files to the working directory:
- LICENSE file, contains the license terms.
- Application .jar file(s) for the main code.
- Any dependencies located in the lib folder.
- You can update these according to your application.
5. The JPackage Command
jpackage^
--input "%working_dir%/app"^
--dest "%output_dir%"^
--resource-dir "%resources_dir%"^
--temp "%working_dir%/temp"^
--name "%name%"^
--vendor "%vendor%"^
--main-jar "%name%.jar"^
--app-version %version%^
--copyright "Copyright (c) %year% %vendor%"^
--icon "%resources_dir%/app.ico"^
--add-modules java.base,java.desktop,java.sql^
--jlink-options "--no-header-files --no-man-pages --strip-debug --strip-native-commands --compress=2"^
--type exe^
--license-file "%resources_dir%/LICENSE.rtf"^
--win-dir-chooser^
--win-menu^
--win-menu-group "%vendor%"^
--win-shortcut^
--win-shortcut-prompt
This command leverages JPackage with detailed options for creating the Windows EXE:
- --input: Specifies the folder containing the JAR files for input.
- --dest: Sets the output directory where the generated installer will be stored.
- --resource-dir: A folder containing additional resources like icons and licenses.
- --temp: Directory to store temporary packaging files.
- --name: Name of the application.
- --vendor: Indicates the application’s publisher.
- --main-jar: The JAR file to launch when the application starts.
- --app-version: Defines the version of the application.
- --icon: Path to the application icon.
- --add-modules: Specifies required Java modules, allowing for smaller, optimized runtime images.
- --jlink-options: Uses jlink to compress the runtime image, strip out debug info, and reduce the size by excluding unneeded headers.
- --type: Specifies the package format (app-image, exe, msi, rpm, deb, pkg, dmg).
- --license-file: Specifies the license file location.
- --win-dir-chooser, --win-menu, --win-menu-group, --win-shortcut, --win-shortcut-prompt: Windows-specific options for installation, such as directory choice, megrouping, and desktop shortcuts.
6. Renaming and Copying the Output Files
ren "%output_dir%\%name%-%version%.exe" "%nospace%-%version%-win-x64.exe"
copy "%working_dir%\temp\images\win-exe.image\%name%-%version%.msi" "%output_dir%\%nospace%-%version%-win-x64.msi"
The ren command renames the EXE output file to a structured format for easier identification. copy command saves a copy of the .msi file in the output directory for distribution.
7. Compressing with 7-Zip
%seven_zip% a "%output_dir%\%nospace%-%version%-win-x64.zip" ".\%working_dir%\temp\images\win-msi.image\%name%\*"
This command uses 7-Zip to create a compressed ZIP file with a portable version of the application, allowing for easier distribution.
8. Final Cleanup
rd "%working_dir%\app" /s /q
rd "%working_dir%\temp" /s /q
These commands remove the temporary directories created during the process to keep the project clean.
Final Thoughts
JPackage is a valuable tool for simplifying the distribution process for Java applications, providing a native, user-friendly experience across platforms. Whether you’re developing a small utility or a large-scale application, JPackage can streamline deployment, making your Java apps accessible and easy to install for end users.
There are many other options available for customizing your package, just change the options at the jpackage command in the script. For example, you can:
- Add Additional Modules: Customize --add-modules to include any Java modules your app requires.
- Change Output Formats: Modify the --type option to generate MSI instead of EXE if needed.
- Adjust Licensing and Icon Options: To ensure the license file and icon paths match your project structure.
Explore the official documentation to learn more about the full range of options for customization.
Thank you for reading, and I hope this guide helps you in getting started with JPackage!
Last updated on November 6, 2024