Java 9, after many delays and failed votes, looks to be finally arriving this September.
Java 9 will bring several new features: enhancements to Streams, a REPL, improvements to Collections, among others. But by far the biggest and most controversial change is Jigsaw. Jigsaw is introducing modularity to the JDK, a long topic in and of itself, but it is one of the major reasons upgrading to Java 9 will be more difficult than previous major releases of Java.
Luckily over recent months several changes, notably disabling the encapsulation features of Jigsaw by default, will make migrating to Java 9 easier. Still despite this a few challenges remain.
In this blog we will take a look at some of the benefits of running in a Java 9 environment, how to migrate a Spring Boot application to Java 9, and finally review some of the common problems you may run into and strategies for resolving them.
However before we begin a quick disclaimer. I will be demonstrating migrating Spring 4.x/Spring Boot 1.x applications to Java 9. As far as I have heard there will be little to no formal support for either in Java 9. So far I haven’t run into many issues once I have gotten the applications running, however as these are primarily proof of concept, they have not been thoroughly vetted.
An Upgrade With Benefits
Disclaimers disclaimed, what are the advantages for migrating to Java 9? A few of them are:
- Better Use of Memory
- Better Performance
- Better Use of Hardware
- Better Documentation
- Prettier Graphics
- Faster Compilation
These are improvements from simply running an application in a Java 9 environment and leaving the code unchanged. If you want more in-depth information on Java 9 performance improvements I would recommend this great blog article by Nicolai Parlog.
With Jigsaw everything is a module. To help with migrating to Java 9, three different types of modules were introduced; unnamed, automatic, and explicit/named modules.
- Unnamed modules are created from placing a jar on classpath (i.e. what happens in the current Java world),
- automatic modules are created when a jar is placed on the new module path, and finally,
- explicit or named modules, these are modules defined manually with the new module-info.java.
Like most things relating to Jigsaw, the different modules types are a long discussion. There are two key takeaways for this article however. One, when migrating your application it, and its underlying libraries, will become unnamed modules, which by default require all modules on module path. And two, the JDK itself has been broken up into about 75 separate modules. Occasionally your code, or its libraries, will require a class outside of what is available in java.base (on module-path by default). So you might need to add modules to your application, which I will demonstrate below.
Migrating to Java 9
At Keyhole we like to talk about microservices a lot. And for one of our classes we built a demonstration project to show off how a microservices application works. I figured migrating this application to Java 9 would be ideal as it is reasonably complex and makes use of a lot of popular libraries. But, most important of all, it already exists, so I don’t have to spend time creating an arbitrary demo project!
The code demo for this blog can be found here: https://github.com/wkorando/java-9-microservices-demo. For demonstration purposes I’ve set this up as a multi-module project (which I wouldn’t normally recommend for a microservice application), so mvn commands can be run at the project root to build all the microservice projects.
Before we begin migrating to Java 9 we need a JDK 9 runtime installed on our local system. Early access builds are readily available. For this demonstration, I will use the jigsaw early access branch which can be found here: http://openjdk.java.net/projects/jigsaw/ea. For Mac users I would highly recommend the jenv tool, for managing multiple Java versions on your machine.
Step #1: Get Maven Working
For awhile there was some consternation around build tools working in Java 9. Gradle, to my knowledge, still has some issues (Gradle users who are experimenting with Java 9: I would love to hear about your experiences), but Maven 3.0+ does work well with Java 9.
My first attempt at running mvn clean install was met with limited success. A couple of the projects built without issue. However, while unit tests were being run on the khs-eureka project, I got a stacktrace with this line in it:
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBContext
As discussed earlier, some libraries might require classes outside of java.base. In this case the Jersey library is referencing the class JAXBContext, which isn’t in java.base. (Tip: Hibernate is another popular library that also requires access to JAXBContext.)
Luckily figuring out which module you need to add is easy with the new searchable javadoc in Java 9. Simply by heading over the JDK 9 javadoc. Searching for the missing class “JAXBContext” returns the following:
And in the top-right corner is our answer; the module we need is “java.xml.bind.”
To manually add a module there is the new command line flag
--add-modules [module list]
So in our case we will add:
mvn clean install -DargLine="--add-modules java.xml.bind"
With that added, all the services build and all unit tests pass.
Step #2: Get the Services Running
Our services are building, unit tests are passing. Now it’s time to see if our services run.
Like when building, some services started up without issue, but a few ran and threw up a stacktrace at start up:
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.ValidationException
This looks pretty familiar to the issue some projects ran into when being built. The fix is pretty similar as well. When running our java -jar, we will need add the “–add-modules” flag as well. For example:
java -jar --add-modules java.xml.bind khs-api-gateway-1.0.0-SNAPSHOT.jar
That change gets all the applications running. However, if we were to navigate to the application’s home page: http://localhost:8080, we are met with an unsatisfying infinite redirect loop.
This was probably the most difficult issue I dealt with while migrating these applications to Java 9. Unfortunately, unlike the class not found issue, there is no simple way to find a solution. The specific issue I was running into was the result of a incompatibility issue between aspectjweaver 1.8.x and Java 9. Luckily the 1.9.x release (currently in beta) resolved my issue.
Finding this out mostly relied upon trial and error and research. The best direction I can give would be that libraries that make heavy use of reflection, like Aspectj and Hibernate, are going to be the most likely culprits when running into these types of issues migrating to Java 9.
Migration Tips and Tricks
So to review some of the tips and tricks we learned from migrating the demo application to Java 9:
- When you get a class not found error, there is a good chance it is because a library is referencing a class not in a module on module path.
- To find the module you need to add search for the missing class here: http://download.java.net/java/jdk9/docs/api/ and either add or append to the “–add-modules” command line flag.
- Remember that you will likely need to add modules both for when building an application and when running it.
- Libraries that depend heavily upon reflection (like hibernate, lombok, aspectj, etc.) are going to be the most impacted by the move to Java 9. If you are getting unusual runtime errors, those would be the starting points for investigating the issue.
- Update your Maven plugins to a post 3.0+ release as available.
Java 9 is a major version release, so it’s not entirely unfair to expect that there will not be some breaking changes that come with it. I am happy however to see that the amount of work, particularly the amount of change, required is significantly less than what I initially feared. While the best solution will often be to update your dependencies to post Java 9 releases, that might not always be practical.
As we get closer to Java 9 going live, and particularly in a post-release world, migrating to Java 9 should get even easier. Library and tool maintainers will have a finalized spec to update their projects against. There will be more Java 9 activity on Stack Overflow. And more articles and tutorials like this one will start to pop up that cover more use cases of migrating to and running in a Java 9 environment.
I would recommend to start experimenting with Java 9, to see what issues your applications run into. Learning about these problems now could give more time to either design fixes, or provide valuable feedback to library maintainers as they work to upgrade their libraries to be compatible with Java 9.
Having to pass in “–add-modules” directly as a command line flag when running mvn or java -jar is less than ideal. I’ll update the code examples and this blog soon with those values placed in the appropriate locations in the project pom files.