Java 9 introduced a major change in the way Java programs are written, because of the Java Platform Module System (JPMS). And that is not something you can ‘skip’ by moving to a higher Java version; upgrading your software to any version after Java 8 means you have to deal with JPMS.
That is the reason a lot of companies are still stuck at Java 8, but at some point in the future they and everyone else will need to upgrade. Java 11 is the first Long Term Support (LTS) version of Java, and that seems to be the version of choice for a lot of migrations.
Upgrading my open source project, JFXtras, to Java 9 a few years back was a breeze. I have had more problems with the build tool (Gradle) than with the modularization of the code. But that is because the code is fairly new and already modularized using Gradle (Maven) artifacts. But in the last weeks I’ve tried to upgrade a 15+ years old custom made ERP system to Java 11… That turned out not to be a breeze.
Initially I tried to modularize the ERP in the same way as JFXtras, because it just has 3 modules; a business logic jar, a Swing application and an EDI jar (for several batch processes). But each module has grown to use a big number of dependencies, ranging from SOAP support to special Swing components. Some of those dependencies are not supported or developed anymore, so they will not be a Java 9 module.
But JPMS has a nice patch for that, which are called ‘automatic modules’; if a jar does not contain module information, JPMS will use the file name to automatically convert it into a module. It does of course mean that there are no transitive dependencies, but I figured that if I flatten all (transitive) dependencies of the Maven pom, it should work. But numerous conflicts between modules, made it that path sheer impossible. After having spent a number of days on that approach, I gave up.
So I figured that maybe you do not need to modularize the application; after all Java 9 both has a classpath and a module path, and if you keep everything on the classpath than it should just be like pre-Java 9. Right?
Well… Java itself, the JRE, is modularized. And there is no way around that. So even if you put all the dependencies on the classpath, you still have to deal with the modules of the JRE. For my ERP system, this most notably meant running into the fact that A LOT of dependencies either include the java.xml package directly, or refer to a jar containing that package. And that resulted in a lot of ‘split package‘ errors. (Split package means that the same package exists in two modules. In this case the JRE and any jar on the classpath – the unnamed module.)
Funny enough Eclipse was having a problem with that, but Maven / OpenJDK 11 not. I was able to compile and run the application from the command line, but not in Eclipse. It turns out that OpenJDK’s compiler actually has a bug not detecting split packages between the JRE’s and external modules.
Luckily for this situation, it was fairly simple to either exclude the transitive dependencies (like xml-apis and xerces’ xmlParserAPIs) or upgrade the dependencies so this conflict does not occur anymore.
So even though you cannot completely by-pass the modules, this is the easiest path to upgrading legacy software to a post Java 8 version. Next up: getting the tests to run.