In this article, I am going to work through adding modules support to an existing Java application. I am using our JPedal (our Java PDF library). This runs on Java8, but I want users of a later Java JDK to also get the benefit of module support. I can achieve this by creating a multi-jar file using JDeps.
What is Project Jigsaw/Module support (and why do I care)?
In Java9, a new file called module-info.java was added. This allows a jar to specify its dependencies, including which parts of the JVM it uses, and what it exports. This gives 3 very big advantages:-
- Custom version of the JDK for the application. If you only need java.base module, you can have a tiny JDK to ship with your application.
- Much better encapsulation. Classes can now be public internally without being visible to other users. Developers can now share code between packages without it becoming part of the public API.
- Lots of problems simply disappear. module-info defines the required modules, so these can be validated before the application even starts. Most of those messy classpath issues do not happen with modulepath.
Because Java8 will just ignore this file, we can maintain Java8 compatibility while allowing Java9 and above to make use of modules!
How to add module-info to existing code
Java9 and above adds a new tool called jdeps. This is a command line tool to run against a jar file. When run against the jpedal.jar this is what it generates.
At the top, there is a nice summary of JDK modules used
build-jpedal-1.0-SNAPSHOT.jar -> java.base build-jpedal-1.0-SNAPSHOT.jar -> java.datatransfer build-jpedal-1.0-SNAPSHOT.jar -> java.desktop build-jpedal-1.0-SNAPSHOT.jar -> java.logging build-jpedal-1.0-SNAPSHOT.jar -> java.xml build-jpedal-1.0-SNAPSHOT.jar -> not found
followed by a list of which modules are used by each package (or the jar). This makes it really easy to investigate any surprises or oddities in the code (what is java.logging and java.xml being used for in the codebase?)
The unknown also flags up which modules might need to be explicitly declared. It turns out to be some JavaFX code in some display classes. JavaFX is no longer part of the JDK, so it needs to be setup. Having this information in the module files means the JVM can check this before it even runs the code and provide a sensible error message rather than fail when executing the code.
If you run this command with the –list-deps flag you will get just the missing modules
or the list of modules found
The first thing to do is to add in this case is the JavaFX modules. These can be downloaded from here and adding this to modulepath fixes the issue. There is some more documentation on how to use here.
Once this is sorted, jdeps can be used to generate a module-info.java file
This default file exports all packages so the next stage is to decide whether to edit this. This now allows you to tidy up your API and decide what packages can be seen by other applications, and which are internal to your own development.
Using the module file
The last decision is how to add the module-info file to your application. It can just be added to the jar (and Java8 will ignore it). A slightly more complex, but more elegant way, is to make use of the multi-jar feature. This allows for Java8 versions of all your code (with some optimised versions of any classes for use on later versions of Java). This is the approach adopted for JPedal.