This is how you can setup a Maven project for Java 9
One of the most anticipated events of the Java world is going to be when version 9 gets released on the 21st of September*. It’ll be a game changer, no doubt about that, primarily due to its module system. It can put an end to the jar-hell we might have been facing for a long time. In this short tutorial, I’d like to show you how a new Java 9 enabled Maven project can be configured.
* The GA was originally expected to be made available on the 27th of July, however Mark Reinhold has requested another 8 weeks delay in order to go over all the JCP processes.
You can find a sample project here: https://github.com/springuni/springuni-java9.
We could have created multi module projects in Maven (and also with Gradle, Ant, etc.) in the past as well, however they weren’t modules strictly speaking. Altought they appeared to be separate code bases, there was no standard way to define which functionalities those module provided and required. With build systems you could declare dependencies, but modules themselves didn’t take care of managing their own dependencies and offered services.
In large projects developers quickly experienced the jar-hell when multiple versions of the same library got pulled in as a side effect of transitive dependencies.
The way how Java 9 changes mitigates this is that you can now formally declare modules with
module-info.java which encapsulates the following pieces of information.
- Modules’s name
- Other modules this module depends on
- Packages this module provides for other modules
Lots of well known libraries are expected to migrate to Java 9’s module system and as a result they can share their public API and hide their internals with this mechanism.
There’s one caveat however, what are you going to do if you need to use such a library in the future which hasn’t been made available as a module yet? That’s a legitimate question indeed and here come the module types into play.
Modules are just plain old JAR files as before, but as of Java 9 they will contain a special module-info.java I mentioned above. There’s also a new concept called the module-path, which is a sibling of the well known class-path.
Having that said there are four module types:
- Named modules (also known as application modules) containing the aforementioned module-info.java
- Platform modules (similar to the former one, but these are shipped with the JDK)
- Automatic modules are those old JAR made available on the module path
- Unnamed module is everything listed on the standard class-path
Implications for Maven users
Build tools like Maven has to handle the new constructs of the JDK, that is,
module-info.java and module path. They have a Wiki page which lists all the plugin requirements for Java 9.
In order to be able to work with JDK 9’s module system
maven-compiler-plugin version 3.6.1 or later is required.
This is more or less optional, however I’d strongly recommend to use it. Java 9 hasn’t been released yet, we’re still using Java 8 (or maybe Java 7) for production projects and it’s uncomfortable changing all the environment variables and point them to a JDK 9’s home directory all the time we want to experiment with it.
maven-toolchains-plugin enables you to use various environment effortlessly instead.
%USERPROFILE%\.m2\toolchains.xml on Windows) if you haven’t had it yet.
Change the path to your actual JDK installation. After that the toolchains plugin can be added to your project.
Enable Java 9 language support
As of JDK 9 b72, a new feature became available: the
javac --release command line option. In brief, to use
javac to cross-compile to an older release of the platform it is not sufficient to just set the
-target options to the older value; the
bootclasspath must also be set to correspond to the older release too. Setting the
bootclasspath was often forgotten and acquiring the needed information could be inconvenient.
--release flag in
javac addresses both of these shortcomings. Only a single flag needs to be set to cross compile compared to three flags (
-bootclasspath) and the needed information is included in the JDK. The accepted argument values for
--release are 6, 7, 8, and 9.
maven.compiler.release is directly mapped to the
--release flag of
javac, while the other two properties are only necessary for IntelliJ to understand source compatibility.
Unfortunately there are test compilation failures at the time of writing. This is due to a breaking change introduced by JDK-8178012, which removed of the
-Xmodule compiler flag. Hopefully MCOMPILER-294 gets fixed soon, in the meantime however, compiling test sources can be disabled.
That’s all folks! 🙂