In this post we will introduce the Java Platform Module System (JPMS) which is the biggest change in the Java 9 release. In this post we will take a look at some basics of JPMS (Why do we need modules? What has changed to the JDK?). After that, we will take a look at how a single module application can be created, compiled and executed. At the end, we will take a look at how a multi module application can be created, compiled and executed. In this post we will only use command line tools. The examples used, can be found on GitHub.

Why?

Let’s start with the fundamental question: why do we need modules anyway? In order to answer that question, we take a look at JSR-376, in which we can find the answer. The following bullets are copied from the JSR:

  • Reliable configuration — Developers have long suffered with the brittle, error-prone class-path mechanism for configuring program components. The class path cannot express relationships between components, so if a necessary component is missing then that will not be discovered until an attempt is made to use it. The class path also allows classes in the same package to be loaded from different components, leading to unpredictable behavior and difficult-to-diagnose errors. The proposed specification will allow a component to declare that it depends upon other components, as other components depend upon it.
  • Strong encapsulation — The access-control mechanism of the Java programming language and the Java virtual machine provides no way for a component to prevent other components from accessing its internal packages. The proposed specification will allow a component to declare which of its packages are accessible by other components, and which are not.
  • A scalable platform — The ever-increasing size of the Java SE Platform has made it increasingly difficult to use in small devices despite the fact that many such devices are capable of running an SE-class Java virtual machine. The Compact Profiles introduced in Java SE 8 (JSR 337) help in this regard, but they are not nearly flexible enough. The proposed specification will allow the Java SE Platform, and its implementations, to be decomposed into a set of components which can be assembled by developers into custom configurations that contain only the functionality actually required by an application.
  • Greater platform integrity — Casual use of APIs that are internal to Java SE Platform implementations is both a security risk and a maintenance burden. The strong encapsulation provided by the proposed specification will allow components that implement the Java SE Platform to prevent access to their internal APIs.
  • Improved performance — Many ahead-of-time, whole-program optimization techniques can be more effective when it is known that a class can refer only to classes in a few other specific components rather than to any class loaded at run time. Performance is especially enhanced when the components of an application can be optimized in conjunction with the components that implement the Java SE Platform.

Modules – some basics

Java Modules

In order to see which modules are available within Java, we can enter the following command:

java --list-modules

A list is shown with the available modules, below an extract from the list:

java.activation@9
java.base@9
java.compiler@9
...

From the list, we can see that the modules are split into 4 categories. The ones starting with java (standard Java modules), the ones starting with javafx (JavaFX modules), the ones starting with jdk (JDK specific modules) and the ones starting with oracle (Oracle specific modules). Each module ends with @9 indicating that the module belongs to Java 9.

Module declaration

Module properties are located in a file module-info.java. In order to see the description of the module which is defined in this file, the following command can be used:

java --describe-module java.sql

This will output the following:

java.sql@9
exports java.sql
exports javax.sql
exports javax.transaction.xa
requires java.logging transitive
requires java.xml transitive
requires java.base mandated
uses java.sql.Driver

The exports keyword indicates that these packages are available to other modules. This means that public classes are default only public within the module unless it is specified within the module info declaration.

The requires keyword indicates that this module depends on another module.

The uses keyword indicates that this module uses a service.

JavaDoc

Take also a look at the Java 9 JavaDoc. As you will see, it is now divided into modules instead of into packages. Inside a module’s JavaDoc you find a Module Graph and the packages. Actually, the same structure as above with –describe-module can be found.

Create your own JRE

The directory structure of the JDK has changed a bit. A directory jmods is added. This directory contains for each module a jmod file. A jmod file is like a jar file, but then for modules. You can unzip the file and look at the contents. It contains the compiled classes for this module.

The size of the whole JDK is 482 MB on my laptop. When using jlink, we can create our own JRE with only the modules we are using in our application. The following command will create a JRE with only the java.base module.

jlink --module-path ../jmods --add-modules java.base --output d:\java\jre

The size of this runtime is only 36 MB. Pretty easy isn’t it? 🙂

Hello Modules!

Now it’s time for some real action 😉

Example explained

In the next sections we will use a Hello Modules example. The following directory structure will be used.

mods
src
|_ com.mydeveloperplanet.jpmshello - module-info.java
    |_ com
        |_mydeveloperplanet
           |_ jpmshello - HelloModules.java
target

The mods directory will be used for the compiled modules and classes.

The src directory will be used for our sources. Inside this directory we first have a directory com.mydeveloperplanet.jpmshello. This is the top-level directory for our module. It uses the same naming convention as for packages. Inside this directory a file module-info.java exists. The content will be shown and explained later on. Inside the module directory we have the package structure which is already familiar to us. We have one HelloModules.java class. The target directory will be used to store our jar-file in.

Below the contents of the HelloModules.java class is shown. It is a simple HelloModules example. One Main class and it resides in package com.mydeveloperplanet.jpmshello.

package com.mydeveloperplanet.jpmshello;

public class HelloModules {
 
  public static void main(String[] args) {
    System.out.println("Hello Modules!");
  }
 
}

The contents of the module-info.java is as follows.

module com.mydeveloperplanet.jpmshello {
  requires java.base;
}

After the keyword module the name of our module is set. Inside the brackets, we define which standard Java modules we use in our module. In our case, we only use the java.base module. We define the usage by using the keyword requires. It is not necessary to explicitly add the java.base module because it is always implicitly added. But in our example and for clearness, we add it anyway.

Compilation

Next step is to compile the classes. In this post we will use the command line in order to compile and execute the application. It is always a good thing to do this. This way you better understand how things work. Later on, we will take a look at the IDE support and how it works when using Maven.

With the following command we compile the application. The -d option specifies the destination directory for the compiled classes. In our case, we compile it to a subdirectory com.mydeveloperplanet.jpmshello which resembles our module. At the end, we specify the classes to be compiled.

javac -d mods/com.mydeveloperplanet.jpmshello src/com.mydeveloperplanet.jpmshello/module-info.java src/com.mydeveloperplanet.jpmshello/com/mydeveloperplanet/jpmshello/HelloModules.java

Execution

For executing the compiled classes, we need to set the module-path. This option sets the directories where the modules can be found. The module option sets the main class which must be invoked. It is necessary to set the module and the package and the class where the main class is located.

java --module-path mods --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules

The output is:

Hello Modules!

Create a jar file

The above example is already cool, but we also want to package the application in a jar file in order to be able to distribute it easily. We create the jar file with the command below (prerequisite is that the target directory exists). The file option specifies the location and name of the jar file. The main-class option specifies the entry point of the application, in other words where the main class resides. The C option specifies the location of the classes to include in the jar file.

jar --create --file target/jpms-hello-modules.jar --main-class com.mydeveloperplanet.jpmshello.HelloModules -C mods/com.mydeveloperplanet.jpmshello .

Now we can execute the application from the jar file.

java --module-path target/jpms-hello-modules.jar --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules

The output is again:

Hello Modules!

We can also check the module description as we did before with the Java standard modules:

java --module-path target/jpms-hello-modules.jar --describe-module com.mydeveloperplanet.jpmshello

This outputs the following as expected.

requires java.base
contains com.mydeveloperplanet.jpmshello

In the first section we created a custom JRE with only the java.base module. Now run the jar file using this custom JRE. As you will see, the output prints again Hello Modules!

Import package from another module

We will extend the example by using a package from another module than java.base. We will do so by using the java.xml.XMLConstants.XML_NS_PREFIX and print it to the console. The HelloModules.java class becomes the following.

package com.mydeveloperplanet.jpmshello;

import static javax.xml.XMLConstants.XML_NS_PREFIX;

public class HelloModules {
 
  public static void main(String[] args) {
    System.out.println("Hello Modules!");
    System.out.println("The XML namespace prefix is: " + XML_NS_PREFIX);
  }
 
}

Compile it just as we did before. We did not import the javax.xml module, so it is expected that the compilation fails. It does so with the following error on the import statement.

error: static import only from classes and interfaces

Of course we know how to fix this, we have to indicate in the module-info.java that we require the java.xml module. It becomes the following.

module com.mydeveloperplanet.jpmshello {
  requires java.base;
  requires java.xml;
}

Now compile, create the jar file and run the application. The output is:

Hello Modules!
The XML namespace prefix is: xml

Create a multi-module application

Let’s take it one step further. We are going to add another module com.developerplanet.himodule to the application and will use a class HiModules from the other module in our main module. The directory structure becomes now:

mods
src
|_ com.mydeveloperplanet.jpmshello - module-info.java
|   |_ com
|       |_mydeveloperplanet
|          |_ jpmshello - HelloModules.java
|_ com.mydeveloperplanet.jpmshi - module-info.java
    |_ com
        |_ mydeveloperplanet
            |_ jpmshi - HiModules.java
target

The contents of HiModules.java is the following:

package com.mydeveloperplanet.jpmshi;

public class HiModules {
 
  public String getHi() {
    return "Hi Modules!";
  }
 
}

The contents of  module-info.java is the following:

module com.mydeveloperplanet.jpmshi {
}

We extend our HelloModules.java class in order to use the HiModules class.

package com.mydeveloperplanet.jpmshello;

import com.mydeveloperplanet.jpmshi.HiModules;
import static javax.xml.XMLConstants.XML_NS_PREFIX;

public class HelloModules {
 
  public static void main(String[] args) {
    System.out.println("Hello Modules!");
    System.out.println("The XML namespace prefix is: " + XML_NS_PREFIX);
    HiModules hiModules = new HiModules();
    System.out.println(hiModules.getHi());
  }
 
}

Compile a multi-module project

We compile the multi-module project. Compared to the single module project, we change the compilation command as follows:

  • The modules output path (-d option) is changed to mods
  • We add the module-source-path option in order to specify where the modules source path is
  • We add the sources of module com.mydeveloperplanet.jpmshi to the list of the be compiled classes
javac -d mods --module-source-path src src/com.mydeveloperplanet.jpmshello/module-info.java src/com.mydeveloperplanet.jpmshello/com/mydeveloperplanet/jpmshello/HelloModules.java src/com.mydeveloperplanet.jpmshi/module-info.java src/com.mydeveloperplanet.jpmshi/com/mydeveloperplanet/jpmshi/HiModules.java

Compilation fails with the following error:

error: package com.mydeveloperplanet.jpmshi is not visible

We forgot two things:

  • We need to tell module com.mydeveloperplanet.jpmshello that it requires module com.mydeveloperplanet.jpmshi
  • We need to tell module com.mydeveloperplanet.jpmshi that package com.mydeveloperplanet.jpmshi is exported and therefore may be used by other modules

The module-info.java from module com.mydeveloperplanet.jpmshello becomes:

module com.mydeveloperplanet.jpmshello {
  requires java.base;
  requires java.xml;
  requires com.mydeveloperplanet.jpmshi;
}

The module-info.java from module com.mydeveloperplanet.jpmshi becomes:

module com.mydeveloperplanet.jpmshi {
  exports com.mydeveloperplanet.jpmshi;
}

After these changes, the compilation is successful and the compiled classes can be found in the mods directory.

Create jar files for a multi-module project

Now it is time to create the jar files. For each module, we need to create a jar file.

Create the jar file for the module com.mydeveloperplanet.jpmshi

jar --create --file target/jpms-hi-modules.jar -C mods/com.mydeveloperplanet.jpmshi .

Create the jar file for the module com.mydeveloperplanet.jpmshello. This is the same command as we used for creating the jar file for the single module project.

jar --create --file target/jpms-hello-modules.jar --main-class com.mydeveloperplanet.jpmshello.HelloModules -C mods/com.mydeveloperplanet.jpmshello .

Execute a multi-module project

And now run the application.

java --module-path target --module com.mydeveloperplanet.jpmshello/com.mydeveloperplanet.jpmshello.HelloModules

The output is:

Hello Modules!
The XML namespace prefix is: xml
Hi Modules!

Running this example with our custom JRE fails with the following error.

Error occurred during initialization of boot layer
java.lang.module.FindException: Module java.xml not found, required by com.mydeveloperplanet.jpmshello

This is what we could expect, since the custom JRE did not include the module java.xml.

Let’s create the custom JRE with the module java.xml included.

jlink --module-path ../jmods --add-modules java.base,java.xml --output d:\java\jre

Run the example again with the custom JRE and now it runs successfully!

Summary

In this post we took our first steps within the Java Platform Module System. We took a look at some theory and after that, we created, compiled and executed a single module application. At the end, we created, compiled and executed a multi module application. We covered some basic keywords of the module-info.java class. In this post we did everything by means of the command line, in a next post we will take a look how we can accomplish the above when working with an IDE and Maven.