-
Notifications
You must be signed in to change notification settings - Fork 7
On the Way to the Java Module System
This is an attempt to prepare a migration to the Java Module System (JMS). It is also an effort to get familiar with JMS and its challenges.
We will start with an analysis listing problems related to modularizing the JavaPOS source code for JMS, followed by proposal how the problems may be solved and how to the design modules. After that we will discuss backward compatibility issues, if any, and how to deal with them.
Based on that the first implementation attempt will be made checking whether the design approach fits the reality. New problems arising during implementation will be discussed here.
All the knowledge about the JMS and how to figure out problems and solution schemas has been taken from the Nicolai Parlog's book The Java Module System, Manning Publications, 2019. It is highly recommended to read this book if you are going to modularize your Java code!
The analysis is based on an AdoptOpenJDK distribution for Java 11.
denis@modula:~/javapos-jms$ apt show adoptopenjdk-11-hotspot
Package: adoptopenjdk-11-hotspot
Version: 11.0.5+10-2
Priority: extra
Section: java
Maintainer: AdoptOpenJDK
Installed-Size: 327 MB
Provides: java-compiler, java-sdk, java-sdk-headless, java10-sdk, java11-sdk, java2-sdk, java5-sdk, java6-sdk, [...]
Depends: ca-certificates, java-common, libasound2, libc6, libx11-6, libxext6, libxi6, libxrender1, libxtst6, zlib1g
Homepage: https://adoptopenjdk.net/
License: GPL-2.0+CE
Vendor: AdoptOpenJDK
Download-Size: 198 MB
APT-Manual-Installed: yes
APT-Sources: https://adoptopenjdk.jfrog.io/adoptopenjdk/deb xenial/main amd64 Packages
Description: OpenJDK Development Kit 11 (JDK) with Hotspot by AdoptOpenJDK
The analysis mainly utilizes the jdeps tool from the JDK: alias jdeps='/usr/lib/jvm/adoptopenjdk-11-hotspot-amd64/bin/jdeps'.
At first, we are going to check what we have. All JavaPOS JARs and their dependencies has been copied to a directory for analysis:
denis@modula:~/javapos-jms$ ls
javapos-config-loader-2.3.1.jar javapos-contracts-1.14.3.jar javapos-controls-1.14.1.jar xerces-1.2.3.jar
At a first call to jdeps on javapos-controls JAR we are getting a first overview of the problems we may have:
denis@modula:~/javapos-jms$ jdeps -s --class-path '*' javapos-controls-1.14.1.jar
Warning: split package: javax.xml.parsers jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.events jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.html jrt:/jdk.xml.dom xerces-1.2.3.jar
Warning: split package: org.w3c.dom.ranges jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.traversal jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax.ext jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax.helpers jrt:/java.xml xerces-1.2.3.jar
javapos-controls-1.14.1.jar -> java.base
javapos-controls-1.14.1.jar -> java.desktop
javapos-controls-1.14.1.jar -> javapos-config-loader-2.3.1.jar
javapos-controls-1.14.1.jar -> javapos-contracts-1.14.3.jar
This shows us, we need to get rid of the Xerces JAR of that version as it has massive split package problems with JDK modules. This was assumed as the Xerces JAR is coming as a very old quite out dated version.
Now, it would be good to get this overview information as a dependency graph representation. jdeps together with dot from the graphviz package can do that for us. We start the analysis from the top of the dependency tree, we know the javapos-controls JAR is, and let jdeps traverse the dependency hierarchy recursively which result in the a dots subdirectory with a image of the dependency graph created:
denis@modula:~/javapos-jms$ jdeps -summary -recursive --class-path '*' --dot-output dots javapos-controls-1.14.1.jar && dot -Tpng -O dots/summary.dot && tree
[warnings removed here]
.
├── dots
│ ├── summary.dot
│ └── summary.dot.png
├── javapos-config-loader-2.3.1.jar
├── javapos-contracts-1.14.3.jar
├── javapos-controls-1.14.1.jar
└── xerces-1.2.3.jar
1 directory, 6 files
Resulting in the following dependency graph:

Let's now drill deeper in our dependency hierarchy starting from the base, which we know is the javapos-contracts JAR.
denis@modula:~/javapos-jms$ jdeps --class-path '*' javapos-contracts-1.14.3.jar
javapos-contracts-1.14.3.jar -> java.base
javapos-contracts-1.14.3.jar -> java.desktop
jpos -> java.awt java.desktop
jpos -> java.lang java.base
jpos -> jpos.events javapos-contracts-1.14.3.jar
jpos.events -> java.lang java.base
jpos.events -> java.util java.base
jpos.loader -> java.lang java.base
jpos.loader -> jpos javapos-contracts-1.14.3.jar
jpos.services -> java.awt java.desktop
jpos.services -> java.lang java.base
jpos.services -> jpos javapos-contracts-1.14.3.jar
jpos.services -> jpos.events javapos-contracts-1.14.3.jar
jpos.services -> jpos.loader javapos-contracts-1.14.3.jar
OK, that looks fine. We do not have split package problems with the JDK modules or any "unknown packages" problems.
Continuing with the javapos-config-loader JAR, the next in the dependency hierarchy:
denis@modula:~/javapos-jms$ jdeps --class-path '*' javapos-config-loader-2.3.1.jar
Warning: split package: javax.xml.parsers jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.events jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.html jrt:/jdk.xml.dom xerces-1.2.3.jar
Warning: split package: org.w3c.dom.ranges jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.w3c.dom.traversal jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax.ext jrt:/java.xml xerces-1.2.3.jar
Warning: split package: org.xml.sax.helpers jrt:/java.xml xerces-1.2.3.jar
javapos-config-loader-2.3.1.jar -> java.base
javapos-config-loader-2.3.1.jar -> java.desktop
javapos-config-loader-2.3.1.jar -> java.xml
javapos-config-loader-2.3.1.jar -> javapos-contracts-1.14.3.jar
javapos-config-loader-2.3.1.jar -> xerces-1.2.3.jar
jpos.config -> java.io java.base
jpos.config -> java.lang java.base
jpos.config -> java.lang.reflect java.base
jpos.config -> java.net java.base
jpos.config -> java.util java.base
jpos.config -> jpos javapos-contracts-1.14.3.jar
jpos.config -> jpos.loader javapos-config-loader-2.3.1.jar
jpos.config -> jpos.util javapos-config-loader-2.3.1.jar
jpos.config -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.config.simple -> java.io java.base
jpos.config.simple -> java.lang java.base
jpos.config.simple -> java.net java.base
jpos.config.simple -> java.util java.base
jpos.config.simple -> java.util.zip java.base
jpos.config.simple -> jpos.config javapos-config-loader-2.3.1.jar
jpos.config.simple -> jpos.loader javapos-config-loader-2.3.1.jar
jpos.config.simple -> jpos.util javapos-config-loader-2.3.1.jar
jpos.config.simple -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.config.simple.xml -> java.io java.base
jpos.config.simple.xml -> java.lang java.base
jpos.config.simple.xml -> java.net java.base
jpos.config.simple.xml -> java.text java.base
jpos.config.simple.xml -> java.util java.base
jpos.config.simple.xml -> javax.xml.parsers java.xml
jpos.config.simple.xml -> jpos.config javapos-config-loader-2.3.1.jar
jpos.config.simple.xml -> jpos.config.simple javapos-config-loader-2.3.1.jar
jpos.config.simple.xml -> jpos.util javapos-config-loader-2.3.1.jar
jpos.config.simple.xml -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.config.simple.xml -> org.apache.xerces.dom xerces-1.2.3.jar
jpos.config.simple.xml -> org.apache.xerces.jaxp xerces-1.2.3.jar
jpos.config.simple.xml -> org.apache.xerces.parsers xerces-1.2.3.jar
jpos.config.simple.xml -> org.apache.xml.serialize xerces-1.2.3.jar
jpos.config.simple.xml -> org.w3c.dom java.xml
jpos.config.simple.xml -> org.xml.sax java.xml
jpos.config.simple.xml -> org.xml.sax.helpers java.xml
jpos.loader -> java.io java.base
jpos.loader -> java.lang java.base
jpos.loader -> java.lang.reflect java.base
jpos.loader -> java.net java.base
jpos.loader -> java.security java.base
jpos.loader -> java.util.jar java.base
jpos.loader -> jpos javapos-contracts-1.14.3.jar
jpos.loader -> jpos.config javapos-config-loader-2.3.1.jar
jpos.loader -> jpos.loader.simple javapos-config-loader-2.3.1.jar
jpos.loader -> jpos.profile javapos-config-loader-2.3.1.jar
jpos.loader -> jpos.util javapos-config-loader-2.3.1.jar
jpos.loader -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.loader.simple -> java.lang java.base
jpos.loader.simple -> java.lang.reflect java.base
jpos.loader.simple -> java.util java.base
jpos.loader.simple -> jpos javapos-contracts-1.14.3.jar
jpos.loader.simple -> jpos.config javapos-config-loader-2.3.1.jar
jpos.loader.simple -> jpos.config.simple javapos-config-loader-2.3.1.jar
jpos.loader.simple -> jpos.loader javapos-config-loader-2.3.1.jar
jpos.loader.simple -> jpos.loader javapos-contracts-1.14.3.jar
jpos.loader.simple -> jpos.profile javapos-config-loader-2.3.1.jar
jpos.loader.simple -> jpos.util javapos-config-loader-2.3.1.jar
jpos.loader.simple -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.profile -> java.io java.base
jpos.profile -> java.lang java.base
jpos.profile -> java.net java.base
jpos.profile -> java.util java.base
jpos.profile -> javax.xml.parsers java.xml
jpos.profile -> jpos.util javapos-config-loader-2.3.1.jar
jpos.profile -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.profile -> org.apache.xerces.parsers xerces-1.2.3.jar
jpos.profile -> org.w3c.dom java.xml
jpos.profile -> org.xml.sax java.xml
jpos.util -> java.awt java.desktop
jpos.util -> java.awt.event java.desktop
jpos.util -> java.io java.base
jpos.util -> java.lang java.base
jpos.util -> java.util java.base
jpos.util -> java.util.jar java.base
jpos.util -> java.util.zip java.base
jpos.util -> javax.swing java.desktop
jpos.util -> jpos.config javapos-config-loader-2.3.1.jar
jpos.util -> jpos.config.simple javapos-config-loader-2.3.1.jar
jpos.util -> jpos.loader javapos-config-loader-2.3.1.jar
jpos.util -> jpos.util.tracing javapos-config-loader-2.3.1.jar
jpos.util.tracing -> java.io java.base
jpos.util.tracing -> java.lang java.base
jpos.util.tracing -> java.util java.base
jpos.util.tracing -> jpos.util javapos-config-loader-2.3.1.jar
That's a quite long list. But no additional problem show up we didn't know already -- the split package problem in the Xerces JAR.
Let's get rid of Xerces for the further analysis to see the JavaPOS code internal problems more clearer.
denis@modula:~/javapos-jms$ rm xerces-1.2.3.jar && ls
dots javapos-config-loader-2.3.1.jar javapos-contracts-1.14.3.jar javapos-controls-1.14.1.jar
Finally, the last JAR on the top of the dependency graph is analyzed:
denis@modula:~/javapos-jms$ jdeps --class-path '*' javapos-controls-1.14.1.jar
javapos-controls-1.14.1.jar -> java.base
javapos-controls-1.14.1.jar -> java.desktop
javapos-controls-1.14.1.jar -> javapos-config-loader-2.3.1.jar
javapos-controls-1.14.1.jar -> javapos-contracts-1.14.3.jar
jpos -> java.awt java.desktop
jpos -> java.beans java.desktop
jpos -> java.lang java.base
jpos -> java.util java.base
jpos -> jpos.events javapos-contracts-1.14.3.jar
jpos -> jpos.loader javapos-config-loader-2.3.1.jar
jpos -> jpos.loader javapos-contracts-1.14.3.jar
jpos -> jpos.services javapos-contracts-1.14.3.jar
No new problems detected here.
Now, let's see whether we have a split package problem in the JavaPOS JARs itself. For that, we create a module path directory and move the base dependency JAR javapos-contracts to it. This puts javapos-contracts into the set of automatic modules letting it participate in the module graph construction made by jdeps and is similar to as if we had already modularized javapos-contracts.
denis@modula:~/javapos-jms$ mkdir mods && mv javapos-contracts-1.14.3.jar mods && tree
.
├── dots
│ ├── summary.dot
│ └── summary.dot.png
├── javapos-config-loader-2.3.1.jar
├── javapos-controls-1.14.1.jar
└── mods
└── javapos-contracts-1.14.3.jar
2 directories, 5 files
This results in the following jdeps analysis result for the javapos-controls JAR, the top of our dependency graph:
denis@modula:~/javapos-jms$ jdeps -summary -recursive --class-path '*' --module-path mods javapos-controls-1.14.1.jar
Warning: split package: jpos file:///home/denis/javapos-jms/mods/javapos-contracts-1.14.3.jar javapos-controls-1.14.1.jar
Warning: split package: jpos.loader file:///home/denis/javapos-jms/mods/javapos-contracts-1.14.3.jar javapos-config-loader-2.3.1.jar
javapos-controls-1.14.1.jar -> java.base
javapos-controls-1.14.1.jar -> java.desktop
javapos-controls-1.14.1.jar -> javapos.contracts
javapos-controls-1.14.1.jar -> not found
javapos.contracts -> java.base
javapos.contracts -> java.desktop
Here a new real problem in JavaPOS' package structure shows up. There are split packages when we will have made all the JavaPOS JARs to modules.
- The package jpos is split between javapos-contracts and javapos-controls,
- The package jpos.loader is split between javapos-contracts and javapos-config-loader.
These 2 problems have to be solved by redesign the JavaPOS package structure.
But wait. There is a new "not found" on the table. Where this comes from? Let's make a deeper class level analysis:
denis@modula:~/javapos-jms$ jdeps -verbose --class-path '*' --module-path mods javapos-controls-1.14.1.jar | grep "not found"
javapos-controls-1.14.1.jar -> not found
jpos.BaseJposControl -> jpos.loader.JposServiceConnection not found
jpos.BaseJposControl -> jpos.loader.JposServiceLoader not found
This comes from the fact that javapos-contracts is an automatic module now. jdeps computes the module graph with javapos-contract's package jpos.loader inside. And therefore, the Java module system does not allow to load the classes JposServiceConnection and JposServiceLoader of that package from another JAR (from the 'unnamed' module, to be precise), as those both classes are contained in the javapos-config-loader JAR. This JAR is on the class-path and therefore in the 'unnamed' module.
- Xerces JAR in version 1.2.3 is not JMS compliant and has split packages to several JDK modules.
- The package jpos is split between javapos-contracts and javapos-controls.
- The package jpos.loader is split between javapos-contracts and javapos-config-loader.