Close

Java 9 Modules - The Root Modules

[Last Updated: Oct 11, 2018]

In this tutorial, we will understand what Root Modules are. We will also learn how our old code will continue working using Java EE libraries like JAXB in Java 9.

The default Root modules

In JDK 9, there are a couple of modules which contain only module-info.java and no packages or Java code. Their sole purpose is to require other modules (called root modules) and make them visible outside. 'java.se' is one of such module. Let's see how it is declared:

module java.se {
    requires transitive java.compiler;
    requires transitive java.datatransfer;
    requires transitive java.desktop;
    requires transitive java.instrument;
    requires transitive java.logging;
    requires transitive java.management;
    requires transitive java.management.rmi;
    requires transitive java.naming;
    requires transitive java.prefs;
    requires transitive java.rmi;
    requires transitive java.scripting;
    requires transitive java.security.jgss;
    requires transitive java.security.sasl;
    requires transitive java.sql;
    requires transitive java.sql.rowset;
    requires transitive java.xml;
    requires transitive java.xml.crypto;
}

What is 'requires transitive' clause?

Generally speaking, the effect of 'transitive' is that the target module is not only required by this module (java.se in this case) but will also be readable to other modules which are reading this module. For example 'requires transitive java.logging' will make java.logging available to the module which requires java.se module. At first 'requires transitive' seems to be same as 'exports' but they are different in that 'exports' is used to make packages visible, whereas 'requires transitive' is used to make imported modules visible outside.

How root modules are used?

When the Unnamed Module (last tutorial) is being compiled or loaded, one of the set of root modules should be accessible to the Unnamed module (so that a non-modular application will continue to work). The default set of root modules is implementation specific. In the JDK implementation it is the module "java.se" (the above one).

What if we use a class coming from a module which is not in the set of the default root modules? Let's see that with an example.

Example

This example uses the JAXB API to unmarshal a String to an object.

public class MsgUnMarshaller {
  public static void main(String... st) throws JAXBException {
      JAXBContext jaxbContext = JAXBContext.newInstance(MsgObj.class);
      Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

      StringReader reader = new StringReader("<msgObj><msg>test msg</msg></msgObj>");
      MsgObj msgObj = (MsgObj) unmarshaller.unmarshal(reader);
      System.out.println(msgObj);
  }
}
@XmlRootElement
public class MsgObj {
  private String msg;

  public String getMsg() {
      return msg;
  }

  public void setMsg(String msg) {
      this.msg = msg;
  }

  @Override
  public String toString() {
      return "MsgObj{" +
              "msg='" + msg + '\'' +
              '}';
  }
}

Following is our project structure:

D:\unnamed-and-jee-module-example>tree /F /A
\---src
\---com
\---logicbig
MsgObj.java
MsgUnMarshaller.java

Compiling using javac

D:\unnamed-and-jee-module-example>javac -d out src/com/logicbig/MsgObj.java src/com/logicbig/MsgUnMarshaller.java
src\com\logicbig\MsgObj.java:3: error: package javax.xml.bind.annotation is not visible
import javax.xml.bind.annotation.XmlRootElement;
^
(package javax.xml.bind.annotation is declared in module java.xml.bind, which is not in the module graph)
src\com\logicbig\MsgUnMarshaller.java:3: error: package javax.xml.bind is not visible
import javax.xml.bind.JAXBContext;
^
(package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
src\com\logicbig\MsgUnMarshaller.java:4: error: package javax.xml.bind is not visible
import javax.xml.bind.JAXBException;
^
(package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
src\com\logicbig\MsgUnMarshaller.java:5: error: package javax.xml.bind is not visible
import javax.xml.bind.Unmarshaller;
^
(package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
4 errors

The above exception messages are clear, the JAXB related classes are not visible to our unnamed module. The reason is, the default set of root modules in java.se does not have 'requires transitive java.xml.bind'.

To fix the exception we have to add the module by using '--add-modules java.xml.bind':

D:\unnamed-and-jee-module-example>javac --add-modules java.xml.bind -d out src/com/logicbig/MsgObj.java src/com/logicbig/MsgUnMarshaller.java

It's compiled this time.

Running using java

Now let's run the main class by using 'java' command:

D:\unnamed-and-jee-module-example>java -cp out com.logicbig.MsgUnMarshaller
Error: Unable to initialize main class com.logicbig.MsgUnMarshaller
Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

This exception is also because of the same reason; the module 'java.xml.bind' has not been imported during runtime. In this case, however, the exception is less intuitive than it was during the compilation.

To fix the exception, we have to use '--add-modules' option with java command also:

D:\unnamed-and-jee-module-example>java --add-modules java.xml.bind -cp out com.logicbig.MsgUnMarshaller
MsgObj{msg='test msg'}

How it was working before Java 9?

JAXB API was working with JDK 8 and older versions without any extra configuration. This API and other Java EE APIs have been bundled with Java SE since version 6. In Java 9, it is decided to separate these Java EE APIs from Java SE via new module system (benefit is, it will reduce the size of JRE so that it can run in smaller devices as well). Currently these APIs have been disabled by default as we saw in above example. Also, the modules which are exporting Java EE API (e.g. java.xml.bind) have been individually deprecated for removal in a future release. The separate and standalone platforms of such modules will be released in future, so the applications and libraries using these APIs can eventually migrate to those platforms.

The java.se.ee module

If your current application uses a lot of Java EE API, it will probably fail if you run it in Java 9. For example, if you are using JPA/Hibernate in your application, it will likely to fail during compiling and running. Rather than figuring out individual required modules, one option could be to use the mixed set of Java SE and Java EE modules declared in java.se.ee module. We can enable this set of root modules via '--add-modules' option. Currently java.se.ee is also included in standard JDK 9 but is not enabled by default. Let's see how this module is declared

@SuppressWarnings({"deprecation", "removal"})
@Deprecated(since="9", forRemoval=true)
module java.se.ee {

    requires transitive java.se;

    // Upgradeable modules for Java EE technologies
    requires transitive java.activation;
    requires transitive java.corba;
    requires transitive java.transaction;
    requires transitive java.xml.bind;
    requires transitive java.xml.ws;
    requires transitive java.xml.ws.annotation;

}

As seen above, this module is deprecated and is subject to removal in a future version of the standard JDK.

Setting --add-modules option in IDE

All IDEs have option to set compiler and JVM runtime options. For example in IntelliJ, we can set them as shown:

For compilation:

For running:

Example Project

Dependencies and Technologies Used:

  • JDK 9
Using JAXB API in Java 9 Select All Download
  • unnamed-and-jee-module-example
    • src
      • com
        • logicbig
          • MsgUnMarshaller.java

    See Also