Oct 28, 2015

Java 9 Jigsaw - The long-awaited Java Module System


Java 9 Jigsaw

The long-awaited Java Module System


Introduction


This year at the JavaOne San Francisco, Mark Reinhold and his Team at Oracle presented the Java 9 Module System with the codename Jigsaw. The work on Jigsaw has a long history and finally arrives with Java 9. Since 1999 we develop acyclic software components with Java per convention and learned how to do this with build tools like Ant, Maven or Gradle. In 2005 at QAware, we developed a tool for static bytecode analysis to detect wrong dependencies between packages since the Java language had no idea of controlling package dependencies or avoiding cyclic dependencies between packages and jars. With Maven, things got better. Maven enforces acyclic dependencies between projects but has also no concept to hide certain packages in one Jar. If a public class is accessible, all other public classes are also accessible. Then came OSGi. It seemed to be the answer for this problem, but the bad tool support in IDEs and the over-engineered dynamic model made more problems than the architectural enforcements benefits for most applications. Even so applications exists where OSGi is a perfect choice. So finally Jigsaw arrived. We now get the long awaited Module System in Java 9. The Java language team had not only the architecture of applications in focus, they also divided the fat legacy runtime jar (rt.jar) into modules. Try to load a Java 8 rt.jar file in a tool like Structure101 and you will see a chaotic mess of cyclic and acyclic dependencies. That problem is solved in Java 9. The runtime itself has now a clean separated module dependency architecture. Another big focus of the Java Language team was compatibility and interoperability. Some odd looking design is useful for compatibility reasons between modular and non modular code. In this blog I will show a test-drive of the Java 9 Jigsaw Module System. The Java 9 preview build can be downloaded from here: https://jdk9.java.net/jigsaw

Email Sample Application


We use a simple modular application for sending emails. The application consist of two modules:
  • A mail module with a public interface and a private implementation class. The public part of that module is a package containing a Java interface for sending messages and a factory which returns the implementation to the caller. 
  • A mail client module wich uses the interface of the mail module to send emails. It should be guaranteed that the client can not access the private implementation of the mail module itself. This principle of encapsulation is long known. David Parnas described the principle of information hiding in modules in 1985. 
Our sample application looks like this:
In that situation, the Java 9 Module System can enforce the following:
  • The module MailClient can only use types of exported packages in the module Mail.
  • The module Mail can not have cyclic dependencies to a type located in the module MailClient.
  • Transitive dependencies from Mail to other components can be visible or not for the Module MailClient. This is controlled by declaring a dependency as public in the Mail module.
The Java 9 Module System can NOT enforce: Types of the exported package in module Mail can direct access implementation classes. There is no contract between public and hidden parts of a single module. If you want to write plugin modules which can be replaced, you are not allowed to call any implementation class from the public part of a module. If you want to do this, you have to write a separate API module which looks like this:

Looking at the Code


There is currently no IDE support for the Jigsaw available. Even the latest builds of Netbeans and IntelliJ do not recognize the module-info.java module descriptor file. Since the Java team decided to support multi-module builds with the javac compiler, they had to invent a complete new lookup mechanism: The module path which is separated from the normal classpath. This concept allows to use normal Jars as modules and vice versa. Jars which are located in the module path are called automatic modules. These are modules which can access all public parts of other modules without explicit declaration. This is done mostly to support smart and slow migration to the new module system without too much trouble for existing applications. At the time of writing, there was no support of Maven, Gradle or Ant. So we have to use shell scripts in this demonstration.

To use the Java 9 compiler multi-module build feature we have to make two subdirectories in the src directory: Mail and MailClient. The Mail directory contains the complete source code of the Mail module inclusive the private implementation which should be hidden from a caller. The MailClient directory contains the main class MailClient which uses methods from the public interface of the Mail module. The image shows the directory layout of our sample:

src 
   ├── Mail 
   │    ├── de 
   │    │   └── qaware
   │    │       └── mail
   │    │            ├── MailSender.java
   │    │            ├── MailSenderFactory.java
   │    │            └── impl
   │    │                 └── MailSenderImpl.java
   │    └── module-info.java
   └── MailClient    
       ├── de    
       │   ├── qaware    
       │   └── mail    
       │         └── client                 
       │                └── MailClient.java    
       └── module-info.java

Both modules have the new module descriptor which is per convention in the root package and has the name „module-info.java“. The module descriptor is not a class, enum or interface. It uses the new keywords „module“, „exports“ and „requires“. The module descriptor for the mail and the client are quite simple:

module MailClient {
 requires Mail;
}


module Mail {
 exports de.qaware.mail;
}

In our example the code of client and mail implementation looks like this:

package de.qaware.mail.client;

import de.qaware.mail.MailSender;
import de.qaware.mail.MailSenderFactory;

public class MailClient {

 public static void main(String [] args) {
  MailSender mail = new MailSenderFactory().create();
  mail.sendMail("johannes@qaware.de", 
                              "A message from JavaModule System");

 } 
}


package de.qaware.mail.impl;

import de.qaware.mail.MailSender;

public class MailSenderImpl implements MailSender {
 public void sendMail(String address, String message) {
  System.out.println("Sending mail to: " + address + 
                                          " message: " + message);
 }
}

The Type MailSender and the MailSenderFactory are from the exported package de.qaware.mail of the module Mail. As you might expect, the MailSender Java interface has only one method: sendMail(). The real implementation is in the impl package which non exported in the Mail module and therefore invisible. If you try to access the MailSenderImpl class direct in the client code you get an compiler error:

../MailClient.java:9: error: MailSenderImpl is not visible because package de.qaware.mail.impl is not visible
1 error
Thats exactly what we want. Nobody can violate the access rules of our Mail module. Non exported packages are hidden.

Multi module building, packaging and running


The multi module build is a cool feature of the Jigsaw javac compiler. The compiler command for both modules in one step looks like this:

# compile
javac -d build -modulesourcepath src $(find src -name "*.java")

This command compiles all modules in the subpath of ./src and outputs the resulting classfiles in a identical structure in the folder ./build. So the content of the ./build folder can be simply packed in separate jar files: The jar command creates the module jar file from the compiled output. For the MailClient module we also specify the java main class for the next step.

jar --create --file mlib/Mail@1.0.jar --module-version 1.0 -C build/Mail .
jar --create --file mlib/MailClient@1.0.jar --module-version 1.0 --main-class de.qaware.mail.client.MailClient -C build/MailClient .

Testing
We can now run our modular application by using the module path where the generated jar files are stored. In our example this is the path ./mlib. In this path the both generated jar files are located. So we can now test our application with the following command:

# run
java -mp mlib -m MailClient

Linking


What does linking in Java mean? It means that only the required modules are packed together in a mini application together wich a platform depended starter script and a minified java runtime.

# link
jlink --modulepath $JAVA_HOME/jmods:mlib --addmods MailClient --output mailclient

So the application can be run direct without any knowledge of java or modules. The generated output directory looks like this:

.
├── bin
│   ├── MailClient
│   ├── java
│   └── keytool
├── conf
│   ├── net.properties
│   └── security
│       ├── java.policy
│       └── java.security
├── lib
│   ├── classlist
│   ├── jli
│   │   └── libjli.dylib
│   ├── jspawnhelper
│   ├── jvm.cfg
│   ├── libjava.dylib
│   ├── libjimage.dylib
│   ├── libjsig.diz
│   ├── libjsig.dylib
│   ├── libnet.dylib
│   ├── libnio.dylib
│   ├── libosxsecurity.dylib
│   ├── libverify.dylib
│   ├── libzip.dylib
│   ├── modules
│   │   └── bootmodules.jimage
│   ├── security
│   │   ├── US_export_policy.jar
│   │   ├── blacklist
│   │   ├── blacklisted.certs
│   │   ├── cacerts
│   │   ├── local_policy.jar
│   │   ├── trusted.libraries
│   │   └── unlimited_policy
│   │       ├── README.txt
│   │       ├── US_export_policy.jar
│   │       └── local_policy.jar
│   ├── server
│   │   ├── Xusage.txt
│   │   ├── libjsig.diz
│   │   ├── libjsig.dylib
│   │   ├── libjvm.diz
│   │   └── libjvm.dylib
│   └── tzdb.dat
└── release

The directory tree shows the complete minified runtime for the modular application. In the bin directory there is a generated MailClient starter which can be used to run the application direct without an explicit java command. The log directory contains only the required boot modules and native libraries. After linking you can start the application with the following command:

cd mailclient/bin 
./MailClient
Sending mail to: x@x.de message: A message from JavaModule System

Conclusion


Gratulation to Mark and his Team. You have done an awesome job. Since this feature is very invasive for tools and existing applications, it will take a long time until we can forget the classpath hell. For me personally it is the proof, that our design principles we used in the last 20 years of modular programming are finally arrived the java world as well. Thanks for that.