Part-5: JMPS/Java Modules - multi-release JAR files
Let’s consider the scenario where are required compiled classes against the different Java SE versions: Java SE 8, Java SE 9… Java SE 15 and etc. Since the Java SE 9 it is possible to provide a multi-release JAR files. It means that inside the JAR file are available classes compiled agains different Java SE versions. A JAR file has a content root with classes and resources. It also has a META-INF directory with metadata (MANIFEST.FM file) about the versioning content that can be encoded in compatible way:
example structure:
wits-java-modules-main.jar
├── META-INF
│ ├── MANIFEST.MF
│ └── versions
│ └── 10
│ ├── com
│ │ └── modules
│ │ └── ModulesMain.class
│ └── module-info.class
├── com
│ └── modules
│ └── ModulesMain.class
└── module-info.class
MANIFEST.FM content:
Manifest-Version: 1.0
Created-By: 15 (Oracle Corporation)
Main-Class: com.modules.ModulesMain
Multi-Release: true
Notice that the MANIFEST.FM file does contain the main attribute Multi-Release: true. To create such JAR file the class “ModulesMain.java” is required:
package com.modules;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ModulesMain {
private static final Logger LOGGER = Logger.getLogger(ModulesMain.class.getName());
public static void main(String[] args) {
LOGGER.log(Level.INFO, "Welcome Java Modules!");
LOGGER.log(Level.INFO, "Modular Stones: " + Arrays.asList("Application",
"Libraries","JDK", "Module Aware Tools", "Language & JVM"));
Runtime.Version version = Runtime.version();
String message = " Java version: " + version;
LOGGER.log(Level.INFO, message);
}
}
The file “ModulesMain.java” resides according to the package structure above. Let’s compile the source against a different java versions into different target directories. The compiled classes are prepared for the multi-version JAR file creation:
$java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-1562)
OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
//compile the content root against the Java SE 15
$javac -d ./target ./src/wits-main/module-info.java ./src/wits-main/com/modules/ModulesMain.java
//compile the content against the Java SE 10
$javac --release 10 -d ./target-10 ./src/wits-main/module-info.java ./src/wits-main/com/modules/ModulesMain.java
$jar --create --file wits-java-modules-main.jar --main-class com.modules.ModulesMain -C target . --release 10 -C target-10 .
The JAR file has been created and although all has been compiled by using version Java SE 15, the JAR file is possible to execute using version Java SE 10:
$java -version
output:
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
$java -jar wits-java-modules-main.jar
output:
Oct 08, 2020 12:04:44 AM com.modules.ModulesMain main
INFO: Welcome Java Modules!
Oct 08, 2020 12:04:44 AM com.modules.ModulesMain main
INFO: Modular Stones: [Application, Libraries, JDK, Module Aware Tools, Language & JVM]
Oct 08, 2020 12:04:44 AM com.modules.ModulesMain main
INFO: Java version: 10+46
When the JAR file does not contain a compatible classes the error message is displayed and code is not executed:
$java -version
output:
java version "9.0.4"
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
$java -jar wits-java-modules-main.jar
output:
Error: LinkageError occurred while loading main class com.modules.ModulesMain
java.lang.UnsupportedClassVersionError: com/modules/ModulesMain has been compiled by a more recent version of the Java Runtime (class file version 59.0), this version of the Java Runtime only recognizes class file versions up to 53.0
Main: Java tutorials