1.3.  Describe how a modular project is compiled and run

[Note]

Unnamed module

From Java 9 and forward, all Java classes must be located in a module for the Java VM to use them. But in Java 9 (and Java 11 too) you can still use the -classpath (synonyms: -cp and --class-path) argument to the Java VM when running an application. On the classpath you can include all your older Java classes, just like you have done before Java 9. All classes found on the classpath will be included in what Java calls the unnamed module.

The unnamed module exports all its packages. However, the classes in the unnamed module are only readable by other classes in the unnamed module. No named module can read the classes of the unnamed module.

If a package is exported by a named module, but also found in the unnamed module, the package from the named module will be used.

The unnamed module opens all its packages. However, the classes in the unnamed module are only accessible via Reflection API by other classes in the unnamed module. No named module can access via Reflection API the classes of the unnamed module.

All classes in the unnamed module requires all modules found on the module path. All classes in the unnamed module can read all classes exported by all the Java modules found on the module path.

Let's check scenario when named module code depends on unnamed module code. Create non-modular greeter.jar with the p1.Greeter class:

package p1;

public class Greeter {
    public static void greet() {
        System.out.println("Greeting from Java 8 !");
    }
}
					

C:\1Z0-817\LIB
│   greeter.jar
│
└───p1
        Greeter.java
					

C:\1Z0-817\lib>javac p1/Greeter.java
					

C:\1Z0-817\lib>jar cvf greeter.jar p1
added manifest
adding: p1/(in = 0) (out= 0)(stored 0%)
adding: p1/Greeter.class(in = 409) (out= 288)(deflated 29%)
adding: p1/Greeter.java(in = 138) (out= 113)(deflated 18%)
					

Now create named module for client:

package p2;

import p1.Greeter;

public class Client {
    public static void main(String[] args) {
        Greeter.greet();
    }
}
					

with module definition:

module MOD { }
					

C:\1Z0-817\MOD3
│   module-info.java
│
└───p2
        Client.java
					

Try to compile:

C:\1Z0-817\mod3>javac -cp ../lib/greeter.jar module-info.java p2/Client.java
p2\Client.java:3: error: package p1 is not visible
import p1.Greeter;
       ^
  (package p1 is declared in the unnamed module, but module p1 does not read it)
1 error
					

As you can see, named module MOD cannot access at compile (and runtime) time class from unnamed module added via -cp option.

Now, try the opposite scenario: accessing named module (Greeter) from unnamed module (Client).

Create modular JAR first:

package p1;

public class Greeter {
    public static void greet() {
        System.out.println("Greeting from Java 9 !");
    }
}

					

module-info.java:

module MOD { 
	exports p1;
}
					

C:\1Z0-817\mod4>javac module-info.java p1/Greeter.java
					

C:\1Z0-817\MOD4
│   module-info.class
│   module-info.java
│
└───p1
        Greeter.class
        Greeter.java
					

Create a modular JAR:

C:\1Z0-817>jar --create --file greeter.jar -C mod4 .
					

C:\1Z0-817>jar -tvf greeter.jar
     0 Fri Mar 29 23:16:54 AST 2019 META-INF/
    66 Fri Mar 29 23:16:54 AST 2019 META-INF/MANIFEST.MF
   186 Fri Mar 29 23:16:54 AST 2019 module-info.class
    14 Fri Mar 29 23:13:04 AST 2019 module-info.java
     0 Fri Mar 29 23:14:20 AST 2019 p1/
   409 Fri Mar 29 23:14:20 AST 2019 p1/Greeter.class
   138 Fri Mar 29 23:13:20 AST 2019 p1/Greeter.java
					

Now, create unnamed client module:

package p2;

import p1.Greeter;

public class Client {
    public static void main(String[] args) {
        Greeter.greet();
    }
}
					

C:\1Z0-817\LIB1
└───p2
        Client.java
					

Compile:

C:\1Z0-817>javac -cp greeter.jar lib1/p2/Client.java
					

Create non-modular executable JAR file:

jar --create --file client.jar --main-class p2.Client -C lib1 .
					

C:\1Z0-817>jar -tvf client.jar
     0 Fri Mar 29 23:30:54 AST 2019 META-INF/
    89 Fri Mar 29 23:30:54 AST 2019 META-INF/MANIFEST.MF
     0 Fri Mar 29 23:30:24 AST 2019 p2/
   301 Fri Mar 29 23:30:24 AST 2019 p2/Client.class
   140 Fri Mar 29 22:48:34 AST 2019 p2/Client.java
					

Run the client:

C:\1Z0-817>java -p greeter.jar --add-modules MOD -jar client.jar
Greeting from Java 9 !
					

This demonstrated unnamed module access to named module.

[Important]

When you are trying to launch an application from a non-modular JAR so you have to explicitly resolve required modules by --add-modules

The required named modules have to be on module graph, e.g. add by --module-path (or by -p)

Automatic modules

Automatic modules are named modules which are automatically created from a non-modular JARs. It happens when the JAR is placed on the module path (as dependency) of a modular application via --module-path or -p options.

[Important]

A JAR file that does not have a module-info.class in its top-level directory is a non-modular JAR.

A non-modular JAR becomes modular (automatic module) JAR when used by a modular application via --module-path option (and becomes part of the unnamed module when used by a modular application via --class-path option).

[Important]

Automatic module exports all its packages.

Automatic module requires all other modules on the module path.

Let's create non-modular JAR:

package p1;

public class Greeter {
    public static void greet() {
        System.out.println("Greeting from automatic module !");
    }
}
					

C:\1Z0-817\LIB2
└───p1
        Greeter.java
					

Compile:

C:\1Z0-817\lib2>javac p1/Greeter.java
					

Create JAR:

C:\1Z0-817>jar --create --file greeter-lib.jar -C lib2 .
					

[Important]

--file greeter-lib.jar and --file=greeter-lib.jar are both valid syntaxes.

Get description of the new automatic module:

C:\1Z0-817>jar --file greeter-lib.jar --describe-module
No module descriptor found. Derived automatic module.

greeter.lib automatic
requires java.base mandated
contains p1
					

Create new module application in mod5 directory:

package p2;

import p1.Greeter;

public class Client {
    public static void main(String[] args) {
        Greeter.greet();
    }
}
					

As you saw above, the automatic module name will be greeter.lib and client application requires it:

module MOD { 
    requires greeter.lib;
}
					

Compile:

C:\1Z0-817>javac -p greeter-lib.jar mod5/module-info.java mod5/p2/Client.java
					

C:\1Z0-817\MOD5
│   module-info.class
│   module-info.java
│
└───p2
        Client.class
        Client.java
					

Run:

C:\1Z0-817>java -p greeter-lib.jar;mod5 -m MOD/p2.Client
Greeting from automatic module !
					

Optionally create module JAR and check dependencies:

C:\1Z0-817>jar --create --file client-mod.jar -C mod5 .
					

C:\1Z0-817>jar --file client-mod.jar --describe-module
MOD jar:file:///C:/1Z0-817/client-mod.jar/!module-info.class
requires greeter.lib
requires java.base mandated
contains p2
					

Optionally check dependencies of the modular client:

C:\1Z0-817>jdeps --module-path client-mod.jar;greeter-lib.jar -s --module MOD
MOD -> greeter.lib
MOD -> java.base
					

Automatic module name derivation

If the JAR file has the attribute "Automatic-Module-Name" in its main manifest (META-INF/MANIFEST.MF) then its value is the module name. The module name is otherwise derived from the name of the JAR file.

The version, and the module name when the attribute "Automatic-Module-Name" is not present, are derived from the file name of the JAR file as follows:

The set of packages in the module is derived from the non-directory entries in the JAR file that have names ending in ".class". A candidate package name is derived from the name using the characters up to, but not including, the last forward slash. All remaining forward slashes are replaced with dot ("."). If the resulting string is a legal package name then it is assumed to be a package name. For example, if the JAR file contains the entry "p/q/Foo.class" then the package name derived is "p.q".

The contents of entries starting with META-INF/services/ are assumed to be service configuration files. If the name of a file (that follows META-INF/services/) is a legal class name then it is assumed to be the fully-qualified class name of a service type. The entries in the file are assumed to be the fully-qualified class names of provider classes.

If the JAR file has a Main-Class attribute in its main manifest, its value is a legal class name, and its package is in the set of packages derived for the module, then the value is the module main class.

Java command line options for modules

[Important]

--add-exports, --add-opens, --add-modules, --add-reads, and --patch-module are NOT part of the Java 11 exam.

Professional hosting         Exam 1Z0-817: Upgrade OCP Java 6, 7 & 8 to Java SE 11 Developer Quiz     Exam 1Z0-810: Upgrade to Java SE 8 Programmer Quiz