Skip to content

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.

You should have a Gradle application project with the Jpackage plugin applied. Assumed, the app is called jTunes.

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 --info and 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.

Sample Jdeps output
com.google.common,java.base,java.desktop

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:

Minimal sample module definition
// module-info.java
module 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:

Minimal sample module definition
// module-info.java
module jtunes.app {
requires java.base;
requires com.google.common;
}

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:

build.gradle.kts
application {
mainModule = "jtunes.app"
mainClass = "com.jtunes.app.App"
}

Now it should be possible to build, run, and package this modular jTunes app.

Building the project
gradlew.bat build
gradlew.bat run
gradlew.bat appImage

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.

Sample module definition for a JavaFX app
// module-info.java
module jtunes.app {
requires java.base;
// More requires …
requires javafx.graphics;
opens com.jtunes.app to javafx.graphics;
}

Now jTunes should be a functioning executable module that will be included in the runtime image of its installable app.