Java9 Module System and Self-contained apps

Aug 22, 2018 · 3 min read

I had some hours to spend on coding, and I didn’t want to do any game development, so I went back to Java a little bit, to checkout if I can refactor my 2D engine to support JIGSAW.

My end goal was to be able to ship a standalone, or so called self-contained application, which means the end user won’t need any JRE installed, the app contains everything it needs to run. It wasn’t that complex as the code base was not that big, but I hit some walls.

Set up modules in IntelliJ IDEA with Maven

First, I had to refactor the standard one module maven structure into a multi-modularised structure. As both IDEA and Maven have the term module on top of java modules, it was a little bit confusing.

Here are the steps I did:

  1. Create two new maven module: com.intermetto.engine,
  2. Copy the engine’s code into the engine module, and the game’s code to the game module. Note, that the module name is com.intermetto.engine, but under that I still had to keep the package structure as well — see below.
  3. Create files under module/src/main/java. Note: if you don’t have create in the menu, set the module’s language level at least to 9.
  4. Move the dependencies/resources/tests to the corresponding module’s pom
  5. Delete the root src, resources, test, etc.

At this point, the hierarchy looked like this:

Resolve module dependencies

Next, I hit build, and it helped me what to put in the module-info files. I hit three problems here.

  1. I had to add transitive for some requirements in the engine’s module-info, to be able to use required code in the game’s module without importing those there too.
  2. I had to open some packages for some other packages as those are use reflections.
  3. I had to replace my SLF4J imports to logback one, because the latter one supports only the modules. <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency>

Building Modules

Next step was to build the modules to be able to use jlink. On Udemy, I found a greate course, which helps you to understand how to build modules. Maven also has plugin for building and linking modules, but I found those not maintained.

Linking and automatic modules

Then I tried to link the app with this command:

jlink --module-path $JAVA_HOME/jmods:build/mods --add-modules --launcher --compress 2 --no-header-files --no-man-pages --strip-debug --output build/release

It did not work, because I had an automatic module in the dependencies. Read more here, why automatic modules can’t be linked.

The solution was to update the automatic module jar with a module-info. I followed this article, and it worked!

After this, jlink ran well, and I got my self contained app, which is 28MB! :)


Written by


I'm a father and a senior developer. In my spare time, I'm a hobbyist game developer using OpenGL/LWJGL & Unity