loading...

Report about updating a Java project from JDK 8 to JDK 11

janux_de profile image Jan Mewes ・4 min read

The Java version 8 has reached its end of life. Since January 2019 the free, commercial version doesn't receive updates anymore (see oracle.com).

So (unless you want to switch to non-mainstream JDK providers like AdoptOpenJDK or Amazon Corretto) it's time to upgrade all Java projects to the latest version. Or at least to the latest long-term support version.

This blog post is an experience report about the steps which need to be done on that path.

Assessing status quo

Environment

The project which is going to be updated is rather small. It's a Spring Boot application which uses Apache Wicket as web framework and Gradle for the build management. The primary development environment is a MacBook Pro and the IntelliJ IDE.

Overview over installed JVMs

On MacOS, with this command on the terminal, all JVMs which are installed on the system can be listed:

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (3):
    10.0.1, x86_64: "Java SE 10.0.1"    /Library/Java/JavaVirtualMachines/jdk-10.0.1.jdk/Contents/Home
    9.0.1, x86_64:  "Java SE 9.0.1" /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home
    1.8.0_152, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk-10.0.1.jdk/Contents/Home

In this case they were installed via Homebrew, so an overview can also be provided by the following command:

brew cask list

The JDK which is being used by Gradle is determined by the environment variable JAVA_HOME. With such like instructions in the .bash_profile, the versions can be switched easily:

export JAVA_8_HOME=$(/usr/libexec/java_home -v1.8)
export JAVA_9_HOME=$(/usr/libexec/java_home -v9)
export JAVA_10_HOME=$(/usr/libexec/java_home -v10)

alias java8='export JAVA_HOME=$JAVA_8_HOME'
alias java9='export JAVA_HOME=$JAVA_9_HOME'
alias java10='export JAVA_HOME=$JAVA_10_HOME'

# use JDK 8 by default
export JAVA_HOME=$JAVA_8_HOME

Installing JDK 11

Via Homebrew, the OpenJDK 11 can be installed like this:

brew update
brew tap homebrew/cask-versions
brew search java
brew cask install java11

(See stackoverflow.com) for further details.

Now Java 11 can be added into the .bash_profile:

export JAVA_11_HOME=$(/usr/libexec/java_home -v11)

alias java11='export JAVA_HOME=$JAVA_11_HOME'

After starting a new terminal tab, it should now be ready for use:

$ java11
$ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)

Upgrading the Java project

Upgrade dependencies

My most urgent advice for you today then, is this: If you're using Java 9 or later, upgrade to the latest versions of all your tools and dependencies. -- Mark Reinhold

From Java 8 to Java 9 some breaking changes where introduced in the language. So projects might run into problems, if the libraries they have included depend upon changed language features. So it is a good idea to start the actual migration by updating all the dependencies to their latest version. With Gradle this process is supported by the Gradle Versions Plugin:

$ gradle dependencyUpdates -Drevision=release

------------------------------------------------------------
: Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest release version:
 - com.giffing.wicket.spring.boot.starter:wicket-spring-boot-starter:2.1.5
 - javax.xml.bind:jaxb-api:2.3.1
...

The following dependencies have later release versions:
 - com.google.guava:guava [27.0.1-jre -> 27.1-jre]
     https://github.com/google/guava
 - io.spring.dependency-management:io.spring.dependency-management.gradle.plugin [1.0.6.RELEASE -> 1.0.7.RELEASE]
 - org.projectlombok:lombok [1.18.4 -> 1.18.6]
     https://projectlombok.org
 - org.springframework.boot:spring-boot-gradle-plugin [2.1.2.RELEASE -> 2.1.3.RELEASE]
     https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/spring-boot-gradle-plugin
...
 - org.wicketstuff:wicketstuff-annotation [8.2.0 -> 8.3.0]
     http://wicketstuff.org

Also Gradle should be updated to its latest version:

brew upgrade gradle

Further, the IDE and all its plugins should be up-to-date.

Update the language level in the Gradlefile

Now we are ready to increase the Java version number in the build.gradle file:

subprojects {
    sourceCompatibility = 11
    targetCompatibility = 11

Use JDK 11 in the IDE

Afterwards we need to switch to JDK 11 in IntelliJ. This can be done by going to the menu entry "File > Project structure". The new version and the language level can be selected in the Project SDK dropdown menu. If it is not available yet, it can be added in the "Platform Settings" section.

Screenshot "Project structure" dialog

Also check in the "Preferences" that the new compilation target is being used:

Screenshot "Preferences" dialog

Now build the project and run the unit tests. If you are lucky there are no errors.

Use JDK 11 on the terminal

Then try to do so in the terminal:

gradle clean check

In the case of this project, there was only one problem: The build via the terminal failed because the Lombok library could not be loaded. The fix for this was fairly simple, just adding those two dependency declarations in the build.gradle file:

annotationProcessor("org.projectlombok:lombok:${lombokVersion}")
testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}")

Final test

After all the updates, verify that the application is still running correctly:

gradle clean bootRun -Dspring.profiles.active=qa

No further problem did occur. So all in all, upgrading to Java 11 was easier than expected:

https://github.com/ksch-workflows/ksch-workflows/commit/f5724e627eeaab2b7fb7a2e4a581091f4d0ee628

Cleanup

Last but not least, obsolete Java versions can be removed from the development environment:

Uninstall via Homebrew

brew uninstall java9
brew uninstall java10

Hard deletion

If they where not installed via Homebrew, this is the way to delete old Java versions:

$ cd /Library/Java/JavaVirtualMachines
$ sudo rm -rf jdk-9.0.1.jdk jdk-10.0.1.jdk

Posted on by:

Discussion

markdown guide
 

Did the module system get in your way during the migration? I heard that there are some issues, especially with reflection-heavy packages such as hibernate.

 

No, I didn't experience any problems with the module system. At the moment, I have no intention to use the module path since Gradle multi-project builds in combination with the java-library plug-in seemed to be more suitable for our use-case. So all the libraries are still on the classpath. I guess this circumvents all problems with the JPMS.

Given that Java 9 was released September 2017, I assume that all common, actively developed frameworks and libraries have a solution for API calls which are not allowed anymore. Did you hear of any concrete problem with Hibernate, apart from adding the libraries javax.xml.bind:jaxb-api and org.glassfish.jaxb:jaxb-runtime which are not part of the JDK anymore?

 

But I had problems with the module system 9 month ago, when I evaluated the JPMS vs. Maven submodules vs. Gradle multi-project builds.

See this question on Stackoverflow which is still unanswered:

stackoverflow.com/questions/507872...

It was triggered by dependencies in the Spring framework which didn't like the JPMS constraints.