Migrating from Java 8 to Java 17 with OpenRewrite

In this article we will discuss on how to migrate Java applications from Java 8 to Java 17 using the OpenRewrite migration plugin. At the end of this tutorial, you will learn which are most common challenges that you can face when upgrading to Java 17

Java 8 to Java 17 challenges

In pure project terms, to migrate from Java 11 to Java 17 the first action you need to take is updating the pom.xml from:

<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

To the following:

<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>

Obviously, this is only the tip of the iceberg. There are several migration points you need to cover when migrating to Java 17. For example:

  • Applications using Java EE specifications need to migrate their dependencies to Jakarta EE 8 or newer.
  • Remove deprecated API from earlier versions. Java 9 has deprecated several java.lang Constructors for primitive Classes wrappers (Double, Boolean, Long, Short, etc). Also the java.lang.Security Manager has been deprecated with no replacement for it.
  • Besides java.lang API, you need to check the compatibility with Java 17 of the libraries in your project.For example, if you are using Spring Boot, the minimal System Requirements to run Java 17 is version 2.5 (https://docs.spring.io/spring-boot/docs/2.5.x/reference/html/getting-started.html#getting-started.system-requirements). Another example is Lombok 1.18.12 that is not compatible with Java 17, it cannot generate code as expected. Therefore, you need to update to version 1.18.22.
  • Finally, with Java 1.8 the Java Reflection API could gain access to the non-public class members without limitation. After Java 9, the modular system wants to limit the Reflection API to a reasonable extent.

OpenRewrite plugin to rescue

Clearly, to complete the migration successfully you have to go through the list of deprecated API for Java 17 (https://docs.oracle.com/en/java/javase/17/docs/api/deprecated-list.html) and for the single libraries that you are using.

As a developer, you can use OpenRewrite to solve the most common issues you will encounter with your upgrade.

In order to use it, you need to add the following Maven plugin in your project:

<build>
  <plugins>
    <plugin>
      <groupId>org.openrewrite.maven</groupId>
      <artifactId>rewrite-maven-plugin</artifactId>
      <version>5.2.2</version>
      <configuration>
        <activeRecipes>
          <recipe>org.openrewrite.java.migrate.UpgradeToJava17</recipe>
        </activeRecipes>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>org.openrewrite.recipe</groupId>
          <artifactId>rewrite-migrate-java</artifactId>
          <version>2.0.1</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

Then, you can rewrite your current project using the following goal:

mvn clean install rewrite:run

A practical example

To see in practice how OpenRewrite can help you through the migration of a Java 8 project, let’s add the following Java class to a Maven project:

package com.mycompany.app;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.LoggingMXBean;
import lombok.val;
import java.nio.charset.Charset;
import org.apache.commons.io.Charsets;

public class App {

    Boolean bool = new Boolean(true);
    Byte b = new Byte("1");
    Character c = new Character('c');
    Double d = new Double(1.0);
    Float f = new Float(1.1f);
    Long l = new Long(1);
    Short sh = new Short("12");
    short s3 = 3;
    Short sh3 = new Short(s3);
    Integer i = new Integer(1);

    Charset utf8 = Charsets.UTF_8;

    static void method() {
        LoggingMXBean loggingBean = null;
    }

    void lombok() {
        val foo = "foo";
        List<String> lst = new ArrayList<>();
        for (val s : lst) {}
    }

    void security() {
        SecurityManager security = System.getSecurityManager();
    }
    void divide() {
        BigDecimal bd = BigDecimal.valueOf(10);
        BigDecimal bd2 = BigDecimal.valueOf(2);
        bd.divide(bd2, BigDecimal.ROUND_DOWN);
        bd.divide(bd2, 1);
        bd.divide(bd2, 1, BigDecimal.ROUND_CEILING);
        bd.divide(bd2, 1, 1);
        bd.setScale(2, 1);
    }
    public static void main(String[] args)  {

    }
}

The above Java Class contains a set of deprecated API (primitive Wrappers), some Lombok deprecated API, the deprecated LoggingMXBean Class and relies on the org.apache.commons.io.Charsets to set UTF-8 Charset.

Execute the following Maven goal:

mvn clean install rewrite:run

Here is the Java 17 version of the same Class:

import java.lang.management.PlatformLoggingMXBean;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.nio.charset.Charset;
import org.apache.commons.io.Charsets;

public class App {

    Boolean bool = Boolean.valueOf(true);
    Byte b = Byte.valueOf("1");
    Character c = Character.valueOf('c');
    Double d = Double.valueOf(1.0);
    Float f = Float.valueOf(1.1f);
    Long l = Long.valueOf(1);
    Short sh = Short.valueOf("12");
    short s3 = 3;
    Short sh3 = Short.valueOf(s3);
    Integer i = Integer.valueOf(1);

    Charset utf8 = Charsets.UTF_8;

    static void method() {
        PlatformLoggingMXBean loggingBean = null;
    }

    void lombok() {
        final var foo = "foo";
        List<String> lst = new ArrayList<>();
        for (final var s : lst) {}
    }

    void security() {
        SecurityManager security = System.getSecurityManager();
    }
    void divide() {
        BigDecimal bd = BigDecimal.valueOf(10);
        BigDecimal bd2 = BigDecimal.valueOf(2);
        bd.divide(bd2, RoundingMode.DOWN);
        bd.divide(bd2, RoundingMode.DOWN);
        bd.divide(bd2, 1, RoundingMode.CEILING);
        bd.divide(bd2, 1, RoundingMode.DOWN);
        bd.setScale(2, RoundingMode.DOWN);
    }
    public static void main(String[] args)  {

    }
}

Overall result

As you can see from the resulting Class, we could easily tackle around most of the issues in our code. The list of Java17 recipe in OpenRewrite is not yet exhaustive and there are still use case where you simply cannot migrate automatically. For example, there is no replacement for the *SecurityManager in Java 17.
That being said, OpenRewrite migration tool is an excellent tool to have part of your migration ready in a matter of minutes.

Found the article helpful? if so please follow us on Socials