Java — Recent Developments
Modules & Leaner Runtime
In the last article on “Features to Enhance Developer Friendliness”, I had explored some features introduced in recent versions of Java aimed at making Java more developer friendly.
In this article, let us see Java Platform Modules System (JPMS) and it’s aim of creating leaner runtime.
As part of Java 9, a major change happened within internal JRE library classes. That is the introduction of “Java Platform Modules System” (JPMS) and subsequent re-arranging of all Java internal packages as numerous modules. I say re-arranging because this is not a major refactoring of JRE classes but arranging those packages into relevant modules.
Modules
Modules help to:
- Maintain / segregate dependencies between various modules in an application and those between application modules and library modules.
- Restrict access to internal APIs which should not be exposed to other modules.
- When the same classes / packages are duplicated in the class path ( due to multiple versions of the same library being present or multiple libraries using same package / class names ), the right classes / packages to use is clearly defined during compile time itself. This avoids runtime uncertainties.
- Control which modules can use ‘reflection’ to parse metadata of classes in a particular module.
- Create leaner runtimes using jlink tool.
One can think that Java already has access modifiers (public, private, protected, default) for encapsulation but there is still a gap wherein we can’t expose a class to all packages within a project while ensuring it cannot be accessed by classes outside the project. JPMS aims to bridge this gap.
To begin with, JRE libraries are already modularised as seen below. As can be seen, many straightforward web applications will need only a few of the modules. This provides the opportunity to build smaller application specific JRE and lighter JVM, as the required modules are known in compile time itself and rest can be removed.
In our applications, we can enable this by following below steps. To begin with, the application modules need to be created as separate projects / multi-module projects.
- Each module / project should define a module-info.java under src/main/java. This should include the name of the module, its dependencies and what it exposes. Example:
module com.tc.black.javarevision.jpmslib { requires spring.context;
requires spring.beans; exports com.tc.black.javarevision.jpmslib;
exports com.tc.black.javarevision.jpmslib.external;
exports com.tc.black.javarevision.jpmslib.internal to spring.beans;
opens com.tc.black.javarevision.jpmslib to spring.core;
opens com.tc.black.javarevision.jpmslib.external to spring.core;
opens com.tc.black.javarevision.jpmslib.internal to spring.core;
}
- The root project ( the one with the main method ) will also be defined as a module and this is the main module. With the above example, main module’s module-info.java will look like this:
module com.tc.black.javarevision.jpms { requires spring.boot;
requires spring.boot.autoconfigure;
requires spring.context;
requires spring.web;
requires spring.beans;
requires transitive com.tc.black.javarevision.jpmslib;
requires org.apache.tomcat.embed.core;
exports com.tc.black.javarevision.jpms;
exports com.tc.black.javarevision.jpms.controller;
opens com.tc.black.javarevision.jpms to spring.core;
opens com.tc.black.javarevision.jpms.controller to spring.core;
}
- Another important aspect is that one JAR file can contain only one module.
- Once this multi-module project is built and JAR files generated for all modules, application can be run with the below command:
java -module-path <path to the module JARs including main module JAR> --module com.tc.black.javarevision.jpms/JpmsApplication
JLINK and Other Tools
Along with JPMS, below tools have been introduced which can aid in some related use cases.
- jdeps — helps in analyzing the code base to identify module dependencies
- jdeprscan — helps in analyzing the code base for usage of any deprecated APIs
- jlink — helps in creating a smaller runtime by combining the application’s and the JDK’s modules
- jmod — helps in working with jmod files. jmod is a new format for packaging the modules. This format allows including native code, configuration files, and other data that do not fit into JAR files
We can build a custom JRE with only the required modules for a JPMS multi-module application using the command below:
./jlink --module-path <path to the module JARs including main module JAR> --add-modules <main module name> --output <path where the custom JRE will be created>
JPMS — Barriers for Usage
JPMS and related tools are not used frequently even now though they were released as part of Java 9. There are a few reasons for that:
- A typical application project has a lot of third-party library dependencies and many of today’s most used libraries have not implemented the modules system. Such dependencies will be treated as “automatic modules”; such modules are accessible to all modules and they can access all other modules. This effectively nullifies the encapsulation advantage provided by JPMS.
- One of the key features of JPMS is that it does not allow duplicate packages in a “modulepath”. While this is great for consistency, new project setup with many library dependencies is going to take time as we encounter duplicate packages and cases of multiple versions of the same library in the dependency graph very often.
- jlink is a cool feature in today’s microservices world. But there are many easier alternatives like Micronaut, Quarkus etc.
In the next article in this series “Scripting & Shell Introduction”, I have explored some of the scripting features introduced in Java in recent versions.