App Modularization Tutorial
Jpackage can also package non-modular apps. Here, the main jar is not included in the runtime image but added to the app content. For non-modular apps, the Jpackage plugin uses Jdeps to infer the required modules to be included in the runtime image.
But modularizing Java code is highly recommended and should be done if ever possible. This tutorial demonstrates how the Jpackage plugin can facilitate the modularization a non-modular application.
Prerequisites
Section titled “Prerequisites”You should have a Gradle application project with the Jpackage plugin applied. Assumed, the app is called jTunes.
Dependency Analysis
Section titled “Dependency Analysis”A Java module explicitly declares its required modules. This can be a real hustle, because the module names of many popular libraries are difficult to find, even if they are modularized. If the module name is not documented, it should be findable in the source code, e.g., Guava.
Luckily, this process can be shortened by using the Jdeps tool. We can use the Jdeps output as information source for the module requirements. There are two options:
- Either see the output file:
build/jpackage/jdeps/jdeps-result.txt, - Or run
gradlew jdeps --infoand see the output printed to the console.
The output is a comma-separated list of module names. Keep this list ready for the next steps.
com.google.common,java.base,java.desktopThe module descriptor
Section titled “The module descriptor”To turn jTunes into a module, create a file named module-info.java in the source root.
DirectoryjTunes
Directorysrc
Directorymain
Directoryjava
Directorycom
Directoryjtunes
Directoryapp
- App.java
- module-info.java
- build.gradle.kts
- settings.gradle.kts
Put a valid minimal module definition into this file:
// module-info.javamodule jtunes.app {}A module definition consists of:
- The module name
- Required 3rd-party dependency modules
- Exported packages that are exposed to other modules.
This module won’t work, because it only contains the module name but the dependencies are missing. Add the Jdeps output to the definition, one module per line like this:
// module-info.javamodule jtunes.app { requires java.base; requires com.google.common;}Adding the main module to the build
Section titled “Adding the main module to the build”Gradle needs to know the main module and the main class to build an executable module. So declare it in the Application extension in the Gradle build script:
application { mainModule = "jtunes.app" mainClass = "com.jtunes.app.App"}application { mainModule = 'jtunes.app' mainClass = 'com.jtunes.app.App'}Building
Section titled “Building”Now it should be possible to build, run, and package this modular jTunes app.
gradlew.bat buildgradlew.bat rungradlew.bat appImage./gradlew build./gradlew run./gradlew appImageBonus: JavaFX
Section titled “Bonus: JavaFX”In general, app modules shouldn’t have to export any packages. But JavaFX needs access to the main App class. so if jTunes uses JavaFX, we have to open the respective package for the javafx.graphics module.
// module-info.javamodule jtunes.app { requires java.base; // More requires … requires javafx.graphics; opens com.jtunes.app to javafx.graphics;}Conclusion
Section titled “Conclusion”Now jTunes should be a functioning executable module that will be included in the runtime image of its installable app.