Blog-Archiv

Sonntag, 28. Juni 2020

Run and Deploy Java Modules

After having compiled a Java module the question rises how to launch a Java application built from modules. Further, how to deploy the application, or some of its modules.

Launch a Modularized Java Application

I refer to the example modules I introduced in my recent Blogs about basic dependency declarations. Assume all modules have been compiled into a directory called module-classes in current directory.

module-classes
fri.i18n.messages
fri
i18n
messages
I18nMessage.class
Messages.class
fri.text.format
fri
text
format
Format.class
fri.text.output
fri
text
output
Output.class
fri.application.log
fri
application
log
Logger.class
fri.application
fri
application
Main.class

Launching a Java application in a module then works as follows:

java --module-path module-classes --module fri.application/fri.application.Main

Which is the same as:

java -p module-classes -m fri.application/fri.application.Main

fri.application is the name of the module, fri.application.Main is the fully qualified name of the class containing the public static main method that is the starting point of the application. The dotted module name is separated from the dotted main class name by a slash ('/').

The --module-path (or -p) option denotes the directory where to find modules. It can be a semicolon-separated (';') list of directories in which each one contains one or more modules.

The --module (or -m) option denotes the module-name / fully qualified class-name to launch.

Deployment via JAR Tool

The jar tool got new options for modules. But not everything works as expected: you have to pack each module into its own JAR file, else you will get following error message:

java --module-path module-libs/fri.application.jar --module fri.application/fri.application.Main
Error: Could not find or load main class fri.application.Main in module fri.application

In other words: you can not pack several modules into the same JAR file!

Packing each module into its own JAR screams for a build tool, but for now I will patiently list my commands:

mkdir module-libs
jar --create --file module-libs/fri.i18n.messages.jar -C module-classes/fri.i18n.messages .
jar --create --file module-libs/fri.text.format.jar -C module-classes/fri.text.format .
jar --create --file module-libs/fri.text.output.jar -C module-classes/fri.text.output .
jar --create --file module-libs/fri.application.log.jar -C module-classes/fri.application.log .
jar --create --file module-libs/fri.application.jar --main-class fri.application.Main -C module-classes/fri.application .

The --create option tells the jar command that it should create an archive file.

The --file option denotes the directory and the name of the archive where to write contents into. The directory must exist.

The -C option denotes the directory where following arguments can be found. This option must be the last in the command. Following arguments can be files or directories, directories would be packed with all their contents. In this case, the dot ('.') denotes the current directory, that means everything inside module-classes/fri.i18n.messages would be packed.

The --main-class option denotes the main class (without module name!) in case the module represents an executable application.

After having packed all the module JARs I can launch my application:

java -p module-libs -m fri.application

If I hadn't specified the --main-class option on packing, I'd have to name the main-class after the module name, separated by slash.

New Tools

Deployment for Java apps is supported by some new JDK tools.

jmod

There is a new deployment file-format called JMOD, currently still based on ZIP (like JAR), but this may change in future. JMOD is for compile- and deployment-time, not for runtime. There is a new tool called jmod that lets you create and list JMOD files.

jlink

Since Java 9 you can also build platform-specific standalone Java applications. The new tool called jlink lets you build Java apps for different platforms like WINDOWS, UNIX, and MAC OS-X. It automatically follows dependencies and packs only what is actually needed, for all modules given on command line (ideally just one).

But you have to download the JDK for every platform you want to support, and the standalone-app will contain the JRE of the platform that was jlink'ed to it. That means you will upload three applications when you want to support three platforms, and your user has to decide which one to download. A JRE packed into a jlink'ed application will not be upgraded automatically when the host gets a new JRE.

Mind that the new GraalVM provides compilation of Java code to native platform applications!

Conclusion

Originally Java supported all platforms through the JVM (Java Virtual Machine), which means that I didn't have to care about any target platform as a Java developer. This was a great promise, for me the main reasons to move to Java, and I didn't regret it until today. Most platforms (even Apple!) were willing to provide a JVM implementation, and thus it was enough to deploy JAR, WAR or EAR files.

Deployment over jlink means the user should deinstall the JVM he used until then, because it won't be used any more. Upgrading, configuring or monitoring it would be useless. In turn he gets one JVM per Java standalone-app he downloads. That is what cloud environments demand.

Essentially this is quite a change in the Java concept, where a JVM was meant to run several Java applications. But in reality that didn't happen, lots of software providers shipped their JVM packed with the application. The same applies to web-servers like Tomcat. Next are mobile platforms. Android still has no JVM to drive traditional Java apps, although its architecture adopted lots of Java ideas. The Java runtime library is much too big for such devices. So let's go with time, let's go modular! (Will I ever see my Java app on a mobile?).