Tips for Migrating Past Java 8
Upgrading to the latest version of Java can be a tricky process, especially when you’re stuck on (or before!) Java 8.
This guide intends to offer a brief overview into the general process we’ve taken when making these kinds of upgrades.
Step 0
Before you do anything else, you should establish a baseline for the current state of the project. Ideally, you have automated testing to fall back on. If you don’t have any, now would be a good time to write some.
Either way, make sure you can spin up the project and have an idea of how it already functions, so that you know what you need to verify for.
And most important of all: a pen and paper. This guide can’t cover everything, and if you need to migrate one project, you probably need to migrate several. Keep track of anything unique you run into, so you can be better prepared for next time.
One more note
It will seem very appealing to refactor a project’s code the moment that new JDK features are available. The more modern versions of Java provide a lot of ways to simplify certain aspects of a codebase, and many devs will want to take advantage right away.
Resist this temptation!
If you end up having to revert any of these changes, you want to be able to sort any upgrade issues from any refactoring issues. Spend some time putting the code through its paces, up to and including in production, before you engage in any further reworks.
Baby Steps
Now, if you haven’t already, secure a copy of your new target JDK. A favorite source for fresh builds is adoptium.net (née AdoptOpenJdk). I highly recommend using the latest LTS version for production work. So, at least at time of writing, JDK 25 would be the way to go.
Don’t forget!
- Adjust your
$JAVA_HOMEto the new JDK path - Update your IDE settings to the new JDK path
- Eventually, install it in your deployment environment
- Or update your container’s base image, etc.
Finally, make sure your project is configured to use the new JDK. Beyond your
$JAVA_HOME and IDE settings, if you’re using something like
maven-toolchains-plugin to configure how a project is run, or
maven-compiler-plugin to configure how it builds, you may need to update your
configuration to actually use your new version of Java.
That out of the way, see if you can build your project.
During this process, you may hit:
Errors
Compilation errors are often deeply frustrating. Runtime errors, doubly so. They also tend to be pretty specific to each codebase, so it’s difficult to give any universal advice.
However, painting in broad strokes:
It’s rare that JDK changes directly break an application
Relatively rare, at least. For the most part, changes are lower-level, and you’ll typically spend more time adapting to changes in the libraries you use than you will adapting to Java itself.
Read changelogs
It can (or perhaps will) be tedious, but sometimes it’s the only way to start seeing a path forward. Sometimes you want to upgrade a dependency as far as you can without breaking things. Sometimes you want to start with the minimum possible changes to get a project running. Either way, decide on an approach and stick to it. From there, changelogs will help reduce the guesswork involved in finding the right version for you.
Known Pitfalls — An Ever-Expanding List
Dependencies and Plugins
Several dependencies are known to take issue with newer versions of the JDK. If your project makes use of any of these, you will likely need to upgrade them.
maven-release-plugin >= 3.0.0maven-compiler-plugin >= 3.11.0guice > 56.0.0+ if you needjavax.inject,javax.servlet, orjavax.persistence. 7.0.0+ if you need the jakarta-namespaced equivalents.gson >= 2.9.0Should be drop-in in most cases. See their changelogcommons-io >= 2.7.0lombok >= 1.18.42Lombok versions are usually backwards-compatible, but not forwards-compatible.1.8.42supports up to JDK 25.handlebars.java >= 4.3.0
My tests aren’t running!
In some cases, especially if a JDK migration is accompanied by JUnit 5
migrations, mvn test may fail to run some or all tests driven by JUnit. In
our testing, this can be resolved by adding maven-surefire-plugin to a
project’s build configuration in pom.xml
Cannot invoke javax.script.ScriptEngine.abc() because “xyz” is null
Support for the nashorn JS engine is no longer included in the base JDK. If you have code that depends on it:
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
engine.abc(...);
you will need to add an explicit dependency: org.openjdk.nashorn:nashorn-core >= 15.4
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
Several javax features are no longer built-in past Java 8. Third-party support can be enabled with a few dependencies:
jakarta.xml.bind:jakarta.xml.bind-api >= 2.3.3org.glassfish.jaxb:jaxb-runtime >= 2.3.8
Still stuck?
Navigating upgrades and incompatibilities in both Java and your dependencies can be extraordinarily complex. If you find yourself in need of extra help (or just have a question!) the experts at BHC would be happy to talk to you.