Over a million developers have joined DZone.

Migrating a Spring Boot App to Java 9: Compatibility

DZone's Guide to

Migrating a Spring Boot App to Java 9: Compatibility

Migrating an app to Java 9 and the JPMS should be no problem, right. Not quite. Let's watch everything break, then see what you need to do to fix it.

· Java Zone
Free Resource

Try Okta to add social login, MFA, and OpenID Connect support to your Java app in minutes. Create a free developer account today and never build auth again.

With the coming of Java 9, there is a lot of buzz on how to migrate applications to use the module system. Unfortunately, most of the articles written focus on simple Hello world applications. Or worse, regarding Spring applications, the sample app uses legacy practices — like XML for example. This post aims to correct that by providing a step-to-step migration guide for a non-trivial modern Spring Boot application. The sample app chosen to do that is the Spring Pet clinic.

There are basically 2 steps to use Java 9: first, be compatible then use the fully-fledged module system. This post aims at the former, a future post will consider the later.

Bumping the Java Version

Once JDK 9 is available on the target machine, the first move is to bump the java.version from 8 to 9 in the POM:

    <!-- Generic properties -->

Now, let’s mvn clean compile.

Cobertura’s Failure

The first error along the way is the following:

[ERROR] Failed to execute goal org.codehaus.mojo:cobertura-maven-plugin:2.7:clean (default) on project spring-petclinic:
 Execution default of goal org.codehaus.mojo:cobertura-maven-plugin:2.7:clean failed:
  Plugin org.codehaus.mojo:cobertura-maven-plugin:2.7 or one of its dependencies could not be resolved:
  Could not find artifact com.sun:tools:jar:0 at
   specified path /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/../lib/tools.jar -> [Help 1]

Cobertura is a free Java code coverage reporting tool.
— https://github.com/cobertura/cobertura

It requires access to the tools.jar that is part of JDK 8 (and earlier). One of the changes in Java 9 is the removal of that library. Hence, that is not compatible. This is already logged as an issue. Given the last commit on the Cobertura repository is one year old, just comment out the Cobertura Maven plugin. And think about replacing Cobertura for JaCoCo instead.

Wro4J’s Failure

The next error is the following:

[ERROR] Failed to execute goal ro.isdc.wro4j:wro4j-maven-plugin:1.8.0:run (default) on project spring-petclinic:
 Execution default of goal ro.isdc.wro4j:wro4j-maven-plugin:1.8.0:run failed:
  An API incompatibility was encountered while executing ro.isdc.wro4j:wro4j-maven-plugin:1.8.0:run:
   java.lang.ExceptionInInitializerError: null

wro4j is a free and Open Source Java project which will help you to easily improve your web application page loading time. It can help you to keep your static resources (js & css) well organized, merge & minify them at run-time (using a simple filter) or build-time (using maven plugin) and has a dozen of features you may find useful when dealing with web resources.
— https://github.com/wro4j/wro4j

This is referenced as a Github issue. Changes have been merged, but the issue is still open for Java 9 compatibility should be part of the 2.0 release.

Let’s comment out Wro4J for the moment.

Compilation Failure

Compiling the project at this point yields the following compile-time errors:

Error:(30, 22) java: package javax.xml.bind.annotation is not visible
  (package javax.xml.bind.annotation is declared in module java.xml.bind, which is not in the module graph)
Error:(21, 22) java: package javax.xml.bind.annotation is not visible
  (package javax.xml.bind.annotation is declared in module java.xml.bind, which is not in the module graph)
Error:(22, 22) java: package javax.xml.bind.annotation is not visible
  (package javax.xml.bind.annotation is declared in module java.xml.bind, which is not in the module graph)

That means code on the classpath cannot access this module by default. It needs to be manually added with the --add-modules option of Java 9’s javac. Within Maven, it can be set by using the maven-compiler-plugin:


Now the project can compile.

Test Failure

The next step sees the failure of unit tests to fail with mvn test.

The cause is the same, but it’s a bit harder to find. It requires checking the Surefire reports. Some contain exceptions with the following line:

Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException

Again, test code cannot access the module. This time, however, the maven-surefire-plugin needs to be configured:

        <argLine>--add-modules java.xml.bind</argLine>

This makes the tests work.

Packaging Failure

If one thinks this is the end of the road, think again. The packaging phase also fails with a rather cryptic error:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-jar-plugin:2.6:jar (default-jar) on project spring-petclinic:
 Execution default-jar of goal org.apache.maven.plugins:maven-jar-plugin:2.6:jar failed:
  An API incompatibility was encountered while executing org.apache.maven.plugins:maven-jar-plugin:2.6:jar:
   java.lang.ExceptionInInitializerError: null
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
at org.codehaus.plexus.archiver.zip.AbstractZipArchiver.<clinit>(AbstractZipArchiver.java:116)

This one is even harder to find: it requires a Google search to stumble upon the solution. The plexus-archiver is to blame. Simply bumping the maven-jar-plugin to the latest version - 3.2 at the time of this writing will make use of a Java 9 compatible version of the archiver and will solve the issue:


Spring Boot Plugin Failure

At this point, the project finally can be compiled, tested and packaged. The next step is to run the app through the Spring Boot Maven plugin i.e.mvn spring-boot:run. But it fails again…:

[INFO] --- spring-boot-maven-plugin:1.5.1.RELEASE:run (default-cli) @ spring-petclinic ---
[INFO] Attaching agents: []
Exception in thread "main" java.lang.ClassCastException:
 java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
at o.s.b.devtools.restart.DefaultRestartInitializer.getUrls(DefaultRestartInitializer.java:93)
at o.s.b.devtools.restart.DefaultRestartInitializer.getInitialUrls(DefaultRestartInitializer.java:56)
at o.s.b.devtools.restart.Restarter.<init>(Restarter.java:140)
at o.s.b.devtools.restart.Restarter.initialize(Restarter.java:546)
at o.s.b.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:67)
at o.s.b.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:45)
at o.s.c.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167)
at o.s.c.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at o.s.c.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:122)
at o.s.b.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:68)
at o.s.b.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:48)
at o.s.b.SpringApplication.run(SpringApplication.java:303)
at o.s.b.SpringApplication.run(SpringApplication.java:1162)
at o.s.b.SpringApplication.run(SpringApplication.java:1151)
at org.springframework.samples.petclinic.PetClinicApplication.main(PetClinicApplication.java:32)

This is a documented issue that Spring Boot Dev Tools v1.5 is not compatible with Java 9.

Fortunately, this bug is fixed in Spring Boot 2.0.0.M5. Unfortunately, this specific version is not yet available at the time of this writing. So for now, let’s remove the dev-tools and try to run again. It fails again, but this time the exception is a familiar one:

Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException

Let’s add the required argument to the spring-boot-maven-plugin:

        <jvmArguments>--add-modules java.xml.bind</jvmArguments>

The app can finally be launched and is accessible!


Running a non-trivial legacy application on JDK 9 requires some effort. Worse, some important features had to be left on the way: code coverage and web performance enhancements. On the opposite side, the only meager benefit is the String memory space improvement. In the next blog post, we will try to improve the situation to actually make use of modules in the app.

The complete source code for this post can be found on GitHub


Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

java ,spring boot ,app migration ,java 9 ,jpms ,tutorial

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}