Packaging
As discussed on the console application section, scripts don’t work well when we require more complex runtime components. For example, using the script for a JavaFX application produces an error:
$ ./clock_advanced
Error: JavaFX runtime components are missing, and are required to run this application
Scripts like this are really only suitable for console applications i.e. applications without a GUI. If you are building a JavaFX or Compose desktop application, you need to either use jlink
or jpackage
to build an installer.
JLink will let you build a custom runtime that will handle the module dependencies for JavaFX. The simplest way to do this is to add the JLink
plugin to your build.gradle
file and let Gradle handle it.
plugins {
id 'org.beryx.jlink' version '2.25.0'
}
You can also configure it in the build.gradle
file as well. For a full set of options see the Badass-JLink plugin page.
jlink{
launcher {
name = "clock"
}
imageZip.set(project.file("${project.buildDir}/image-zip/clock-image.zip"))
}
We can rebuild the clock sample using Gradle - build - jLink
to produce a runtime script in build/image
Here’s the resulting directory structure. Notice that it includes a number of libraries that our application needs to run.
$ tree build/image -L 2
AnalogClock/build/image
├── bin
│ ├── clock_advanced
│ ├── clock_advanced.bat
│ ├── java
│ ├── jrunscript
│ └── keytool
├── conf
│ ├── net.properties
│ ├── security
│ └── sound.properties
├── include
│ ├── classfile_constants.h
│ ├── darwin
│ ├── jawt.h
│ ├── jni.h
│ ├── jvmti.h
│ └── jvmticmlr.h
├── legal
│ ├── java.base
│ ├── java.datatransfer
│ ├── java.desktop
│ ├── java.prefs
│ ├── java.scripting
│ ├── java.xml
│ └── jdk.unsupported
├── lib
│ ├── classlist
│ ├── fontconfig.bfc
│ ├── fontconfig.properties.src
│ ├── jrt-fs.jar
│ ├── jspawnhelper
│ ├── jvm.cfg
│ ├── libawt.dylib
..... (continues)
Running the top-level bin/clock_advanced
image will execute our application.
$ ./clock_advanced
Finally, we can use jpackage
to create native installers for a number of supported operating systems. JPackage is included as a console application in Java JDK 16 or higher, and will work with any JVM language (e.g. Java, Kotlin, Scala). The full guide is here.
An installer is an application that when executed, installs a different application for the user. We need installers because most applications consists of many different files: executables, libraries, resources (images, sound files), preference files and so on. These need to be installed in the correct location, and sometimes registered, to function correctly.
Tasks that the installer performs include:
- Copying application files to the correct location.
- Installing and registering system libraries.
- Making changes to the system registry (or similar system databases).
- Creating icons on the desktop, or applications folder.
- Prompting the user if any of these tasks require elevated privileges.
Instead of running jpackage
manually, we will install a plugin into IntelliJ and use that environment to generate our installers. We can do this by installing the Badass-JLink plugin page. To use the plugin, include the following in your gradle.build
script:
plugins {
id 'org.beryx.jlink' version '2.25.0'
}
JPackage itself has a number of other options that you can specify in the build.gradle
file. The full list of options is on the plugin website.
// build.gradle file options for jpackage
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher{
name = 'hello'
jvmArgs = ['-Dlog4j.configurationFile=./log4j2.xml']
}
}
If you install the plugin correctly, then you should see the jpackage
command in Gradle - build - jpackage
. Run this and it will create platform installers in the build/distribution
directory.
This is a standard macOS installer. Drag the clock_advanced icon to the Applications folder. You can then run it from that folder.
Installers are meant for graphical applications. If you are building a JavaFX or Compose desktop application, this is the right choice. If you’re building a console application, you probably want a script instead (see previous step) so that you can execute it from the console directly.
We expect the output of this process to be an installer (or scripts/custom deliverable) for each platform.
An installer does the following:
- Installs your software
- Installs libraries
- Sets the application icon
- Creates shortcuts
- Installs readme.txt
- Installs license.txt
- Adam Carroll. 2021. Installable Java Apps with jpackage. https://vocabhunter.github.io/2021/07/10/installable-java-apps-with-jpackage.html
- Oracle. 2022. Packaging Tool User’s Guide. https://docs.oracle.com/en/java/javase/17/jpackage/packaging-overview.html
- Badass JLink Plugin. https://badass-jlink-plugin.beryx.org/releases/latest/