Part-1: JPMS/Java Modules - introduction

The following section focuses on the basics of the JPMS/Java Modules. The only command line tools and utils provided by Java Platform will be used.  The section uses for demonstration Java SE 15.

$java -version
java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-1562) 

Let’s start with the listing of all available modules within Java Platform. The number after the module name indicates the Java SE version: @15

$java —list-modules

Each Java Module must contain its module descriptor file Such descriptor contains the visibility and the relations dependencies information  to the other modules. Inside the descriptor file ( are used several keyword such as exports, provides, uses etc. (latter explained). To review the module descriptor content the following command:

$java --describe-module java.logging
exports java.util.logging
requires java.base mandated
provides jdk.internal.logger.DefaultLoggerFinder with sun.util.logging.internal.LoggingProviderImpl
contains sun.util.logging.internal
contains sun.util.logging.resources 

Create customized Java Runtime Environment (JRE)

The size of OpenJDK 15 on a Mac system shows about  329MB. Imagine that all modules, event not required ones, will be imported when classpath is used. Introduced Java Platform Module System allows to precisely define which packages should be involved in the target JRE. After the command execution the JRE size is limited to approximately 42MB!

$jlink --module-path $JAVA_HOME/jmods --add-modules java.base --output <CUSTOM_JRE_FOLDER> 

Create the Modules Projects

Let’s create a simple java project without any building tools like a Maven, Gradle or etc. The project resides in the directory “wits-java-modules”. It has the following folder and file structure.

├── src
│   └── wits-main
│       ├── com
│       │   └── modules
│       │       └──
│       └──
└── target 

It is required to defined the module descriptor file and the module dependencies and other packages relations. For such purposes the “requires” keyword is used.

module wits.main {
    requires java.base;

The class file contains the following content:

package com.modules;
import java.util.Arrays;
public class ModesMain {
    public static void main(String[] args) {
        System.out.println("Welcome Java Modules!");
        System.out.println("Modular Stones: " + Arrays.asList("Application", 
        "Libraries","JDK", "Modul Aware Tools", "Language & JVM"));

The project is ready to compile to the target folder.

$javac -d ./target ./src/wits-main/ ./src/wits-main/com/modules/ 

After the compilation the target folder contains the following content

── target
    ├── com
    │   └── modules
    │       └── ModulesMain.class
    └── module-info.class 

The compiled ModulesMain can be executed from the command line by java command with the option —module-path.

$java --module-path ./target --module wits.main/com.modules.ModulesMain
Welcome Java Modules!
Modular Stones: [Application, Libraries, JDK, Module Aware Tools, Language & JVM] 

Pack all to the simple or executable Jar File

Sometimes it is handy to be able to execute a specific compiled class from the command line but most of the time it is required to distribute JAR file artifact. Java JDK comes with JAR util that allows us with several options to create a simple  or executable JAR artifact from the command line.

The Jar file can be created without specifying a Main-Class option:

$jar --create --file wits-java-modules.jar  -C target .
—create : Create the archive
—file <FILE-NAME>: The archive file name
-C <DIR> :  directory and include following file

$java --module-path ./wits-java-modules.jar --module wits.main/com.modules.ModulesMain
—module-path: module path, the value one or more directories that contain modules
—module: [module-name]/<main-class> [args..]  

or as a standalone jar with the specified Main-Class option:

$jar --create --file wits-java-modules.jar --main-class com.modules.ModulesMain -C target .
see above:
—main-class: the application entry point

$java --module-path ./wits-java-modules.jar --module wits.main
- see above: 

After the command  $java —module-path <…chose options> execution, the following is printed to the standard output:

$java --module-path ./wits-java-modules.jar --module wits.main
Welcome Java Modules!
Modular Stones: [Application, Libraries, JDK, Module Aware Tools, Language & JVM] 

To verify the module dependencies, print the module descriptor into the standard output:

$java --module-path wits-java-modules.jar --describe-module wits.main
requires java.base
contains com.modules 

The JDeps tool can be also used to show package dependencies. The JDeps is Java Dependency Analysis command line tool that processes java byte-code. It analyzes the class and package level dependencies:

$jdeps wits-java-modules.jar
   requires java.base (@15)
wits.main -> java.base
   com.modules      ->              java.base
   com.modules      -> java.lang            java.base
   com.modules      -> java.lang.invoke     java.base
   com.modules      -> java.util            java.base 
$jdeps -verbose:class wits-java-modules.jar 
   requires java.base (@15)
wits.main -> java.base
   com.modules.ModulesMain      ->      java.base
   com.modules.ModulesMain      -> java.lang.Object         java.base
   com.modules.ModulesMain      -> java.lang.String         java.base

At the beginning of the section the custom JRE has been created and we have created the standalone wits-main module. The following command execute the standalone module agains the custom JRE:

$<CUSTOM_JRE_FOLDER>/bin/java --module-path wits-java-modules.jar --module wits.main
Welcome Java Modules!
Modular Stones: [Application, Libraries, JDK, Module Aware Tools, Language & JVM]