Upgrading existing apps from Java 8 to Java 11

Upgrading existing apps from Java 8 to Java 11

At Ensolvers, some of our projects we started some years ago were implemented using Java 8 that was the most stable version of the language and virtual machine at that time. However, as time went by, Java 11 took the lead as the official, latest LTS version so we decided to start migrating some of our projects to that version. With this update we obtain, among other advantages reported by Oracle:

  • Improved efficiency
  • jar / war size reduction
  • Language improvements, including streams API. access to new features such as type inference, modularization, etc. – for more detail, check Java 11 Documentation and Guides
  • Addition of a new HTTP client
  • Two new, more efficient garbage collectors
  • New cryptographic algorithms

In our case, we had to implement this migration in several Spring Boot projects (that were using Maven) on the development end and Jetty Web Server (9.4.9) from the infrastructure point of view. Below we describe the step-by-step process we had to do to accomplish the migration successfully.

Step-by-step process

Step 1: Upgrading the Java version

The first thing we need to do is install Java 11 for which there are several alternatives, and some even allow having more than one version, facilitating a quick exchange of versions. In our case we used Jabba, a well-known version manager for Java.

Once Java 11 is installed, we must update in our pom.xml files which version of Java our project / module uses. For this, we will change the value of all the java.version properties and compilerVersion across the project, from 1.8 to 11 .

If you want to preserve IDE-related configuration stored in the project, then you should upgrade the project configuration first. For instance, if you use IntelliJ, you will have to modify the Java version in:

  • File -> Settings -> Build, execution, deployments -> Compiler -> Java compiler -> Project bytecode version

And the configuration for each module of the project in: 

  • File -> Project settings -> Project -> Project SDK and Project language level
  • File -> Project settings -> Modules -> Dependencies -> Module SDK

However, if you don’t use IDE-related configuration (which is what we recommend in order to work in an IDE-agnostic development environment), just re-importing the project from the pom.xml file after updating the compilerVersion variable should do the trick – the rest of the project metainformation should be generated by your IDE automatically.

Step 2: Updating dependencies

One of the changes from Java version 8 to 11, it might happen that some features that your dependencies need may have been removed from the core packages of the SDK. In that case, you are going to find errors of type “Package X cannot be found”. For solving these corner cases, we must research a which extra dependencies we may need to add incorporate them in the pom.xml, as well as changing the corresponding import lines.

In the example below, we show some of the dependencies that we had to update in our pom.xml project.

Step 3: Updating CI/CD processes and infrastructure

Once we already did the update on our project, then we need to promote that into production and assess that everything works correctly. We strongly enforce to start testing these improvements first in your QA/staging environment first.

In our case, we used Jetty 9.4.9 which until recently, used versions of ASM that do not support Java 11, so when unpacking and trying to run our jar or war deliverables, the apps threw several exceptions. One of the solutions, which turns out to be the simplest and recommended, was simply to update the version of Jetty. As of version 9.4.14, jetty has ASM 7 solving this problem. No extra changes have to be made for that upgrade.

We will not go into detail about the update of Jetty since it depends on the methodology and architecture of deployment – and perhaps you don’t use Jetty at all :). In our case, since we have our infrastructure 100% dockerized in all our projects, it just implied a Docker image re-building just changing the FROM directive in our Dockerfile – you can also check our tech note about Docker CI/CD processes here.

Conclusion

The migration process, although it may seem complex before doing it, it just implied the three simple steps described here. This process is applicable to versions 9 and 10 but we do not recommend it since they are not LTS so they will be unsupported in less time than 11 – and, also, not all the libraries that are usually used, maintain compatibility with non-LTS versions.