Java 9 Modules - Automatic Modules

[Updated: Oct 15, 2017, Created: Sep 30, 2017]

In this tutorial, we will understand what Automatic Modules are and how they can help us to use a non-modular third party library jar in our modular application.

What are Automatic Modules?

Automatic Modules are named modules which are automatically created for a non-modular jar. It happens when the jar is placed on the 'module path' (as dependency) of a modular application.

In other words; non modular jar files become modular (automatic module) when used by a modular application.

Let's say we have a modular application 'app' (has the module-info.class) which wants to use a third party non-modular jar 'lib.jar' (does not have the module-info.class). If we run a main class of 'app' say com.app.Main.class using following command:

java --module-path appClasses;lib --module app/com.app.Main

then lib.jar automatically becomes modular for 'app'. This module has the same name as the jar name (extension is dropped, hyphens '-', if any, are replaced by dots '.' and version part, if any, is also dropped). The module 'app' has to still explicitly import the jar module by its name using 'requires' clause in module-info.java.

In above command, appClasses is the folder where 'app' has its complied classes and lib is the folder where we have the third party lib.jar.

Automatic Modules are another migration tool which helps us to develop a modular application or modularize our existing applications without waiting for the third party libraries to become modularized.

What packages it 'exports'?

It 'exports' all its packages.

What modules it 'requires'?

It 'requires' all other modules on the module path.

Example

This example will create two application. The first application will represent a non-modular third party library and the second one will be a modular application which will be using the first library .

The non-modular library

easy-math/src/easy/math/BasicCalc.java

package easy.math;
public class BasicCalc {
  public static int sum(int a, int b) {
      return a + b;
  }
}
D:\automatic-module-example\easy-math>tree /F /A
|
\---src
\---easy
\---math
BasicCalc.java

Compiling the class:

D:\automatic-module-example\easy-math>javac -d out src/easy/math/BasicCalc.java

Creating the jar:

D:\automatic-module-example>jar --create --file easy-math.jar -C easy-math/out .

The modular application

math.app/src/com/example/AppMain.java

package com.example;

import easy.math.BasicCalc;

public class AppMain {
  public static void main(String[] args) {
      int sum = BasicCalc.sum(4, 7);
      System.out.println("sum : " + sum);
  }
}

math.app/src/module-info.java

module math.app {
 requires easy.math;
}

Note that above module declaration requiring the dependency jar module (automatic) by it's name which will transform from 'easy-math.jar' to 'easy.math'

D:\automatic-module-example\math.app>tree /F /A
|
\---src
| module-info.java
|
\---com
\---example
AppMain.java

Moving easy-math.jar (from first project) to math.app application:

D:\automatic-module-example>mkdir math.app\lib
D:\automatic-module-example>move easy-math.jar math.app\lib\
1 file(s) moved.

Now here's our directory structure

D:\automatic-module-example>tree /F /A
|
+---easy-math
| |
| +---out
| | \---easy
| | \---math
| | BasicCalc.class
| |
| \---src
| \---easy
| \---math
| BasicCalc.java
|
\---math.app
|
+---lib
| easy-math.jar
|
\---src
| module-info.java
|
\---com
\---example
AppMain.java

Compiling the application:

D:\automatic-module-example\math.app>javac --module-path lib -d out src/module-info.java src/com/example/AppMain.java

Running the application

D:\automatic-module-example\math.app>java --module-path out;lib --module math.app/com.example.AppMain
sum : 11

Using jdeps

D:\automatic-module-example\math.app>jdeps --module-path lib;out -s --module math.app
math.app -> easy.math
math.app -> java.base

Using --describe-module

For the library jar:

D:\automatic-module-example\math.app>jar --file lib\easy-math.jar --describe-module
No module descriptor found. Derived automatic module.

easy.math automatic
requires java.base mandated
contains easy.math

For the client application:

D:\automatic-module-example\math.app>java --module-path out;lib\easy-math.jar --describe-module math.app
math.app file:///D:/automatic-module-example/math.app/out/
requires easy.math automatic
requires java.base mandated
contains com.example

Creating jar of the main application

Let's convert our main application to jar as well and try --describe-module on the jar:

D:\automatic-module-example\math.app>jar --create --file lib\math-app.jar -C out .
D:\automatic-module-example\math.app>jar --file lib\math-app.jar --describe-module
math.app jar:file:///D:/automatic-module-example/math.app/lib/math-app.jar/!module-info.class
requires easy.math
requires java.base mandated
contains com.example

Using Module API

Let's use new Module API to analyze the modules.

math.app/src/com/example/ModuleAnalysis.java

package com.example;

import easy.math.BasicCalc;

public class ModuleAnalysis {

  public static void main(String[] args) {
      analyze(ModuleAnalysis.class);
      System.out.println("------");
      analyze(BasicCalc.class);
  }

  private static void analyze(Class<?> cls) {
      System.out.println("class: " + cls);
      Module module = cls.getModule();
      System.out.println("Module: " + module);
      System.out.println("Name: " + module.getName());
      System.out.println("isNamed: " + module.isNamed());
      System.out.println("Descriptor: " + module.getDescriptor());
      System.out.println("isAutomatic: " + module.getDescriptor().isAutomatic());
  }
}

We just need to compile above class without recompiling the rest:

D:\automatic-module-example\math.app>javac --module-path lib -d out src/com/example/ModuleAnalysis.java
D:\automatic-module-example\math.app>java --module-path out;lib --module math.app/com.example.ModuleAnalysis
class: class com.example.ModuleAnalysis
Module: module math.app
Name: math.app
isNamed: true
Descriptor: module { name: math.app, [easy.math, mandated java.base (@9)] }
isAutomatic: false
------
class: class easy.math.BasicCalc
Module: module easy.math
Name: easy.math
isNamed: true
Descriptor: module { name: easy.math, [mandated java.base] }
isAutomatic: true

Example Project

Dependencies and Technologies Used :

  • JDK 9
Java 9 Automatic Example Select All Download
  • automatic-module-example
    • easy-math
      • src
        • easy
          • math
    • math.app
      • src
        • com
          • example

See Also