Java 8 to 11: A Migration Story

Authored by Abhishek Shukla, Sr. Software Engineer

Java 8 is at its end-of-life. Public updates have been discontinued as of January 2019, making it even more important to move on to the next long-term-support version, Java 11.

Unlike the previous versions, the migration to the newer releases have some challenges. But fear not, this blog will address the disruptive features of the newer Java versions and the challenges in migration.

JDK, Releases and Licenses: Java has been going through a lot of changes now and there is a new release cadence to Java.

  1. A new version of Java to be released every 6 months - March and September.
  2. Two minor updates to the said releases in one and four months.

With Java 11, Oracle JDK will be released under a commercial license and developers can use OracleJDK for free for development but not for production.

Oracle is also releasing Oracle OpenJDK which can be used for free, though the user will have to follow their 6-month release cycle. This means one can use it for free provided they update every 6 months. This will include LTS (Long Term Support) versions of Java.

Apart from the JDK builds there would be OpenJDK builds released by other providers such as AdoptOpenJDK, Azul, IBM, Red Hat, etc. These builds are implementations of Java Specification, having passed the Java Technology Certification Kit and can be used freely.

Now let’s address the elephant in the room and talk migration from Java 8 to 11.

To make the migration less troublesome, Java 11 supports both classpath and module-path based configuration. Hence it is not required to modularize the project completely while migrating to Java 11. One can follow an incremental approach and migrate the project in phases.

Following these steps will help:

  1. Update all dependencies to the latest versions.
  2. Compile the project and resolve all issues related to unofficial access, split packages and deprecated/removed Java EE modules.
  3. Modularize the application to use the Module system.

Update Everything!

Update all your IDEs, build tools and application dependencies to use the latest version. Updating the dependencies is not required at this time, but it will save a lot of trouble if the used version does not support Java 11.

Following are some of the tools and the recommended version for Java 11.

  • IntelliJ IDEA: 2018.2
  • Eclipse: Photon 4.9RC2 with Java 11 plugin
  • Maven: 3.5.0
    • compiler plugin: 3.8.0
    • surefire and failsafe plugins: 2.22.0

Compile the application:

Compiling the application on Java 11 can result in raising a few issues. Here is a pre-emptive look at some of those issues and how they can be fixed.

Unofficial access to Internal APIs:

Many developers have been using classes from packages which were intended for internal for the JDK. And as Java core libraries are already modularized, with strong encapsulation in place, these packages are no longer available for “public” use. One example is the Base64Encoder in the package “sun.misc”.

If your application is using any of these internal packages, the compiler would flag these as errors :

 

Java-8-to-11

JDeps:

Java-8-to-11

JDeps is a tool provided by the JDK that can be used to scan the code and dependent jar for this unofficial access and other issues.

Using jdeps on Guava (version 18) library, we can see the unofficial access to internal JDK packages by older versions of Mockito and Guava.

Java-8-to-11

Fix:

  1. If the issue is flagged in a dependent JAR, updating to the latest version should resolve the issue, provided that the latest version has been released for JDK 9+.
  2. If the issue is flagged in the application code, then replacing the -access classes with alternatives should resolve the issue. For example, the “Base64Encoder” can be replaced with “Base64.getEncoder()” and the issues should be resolved.
  3. There is a temporary fix available as a command-line directive. Using “--add-exports” option during compilation and runtime, would open up the internal package to be used by the unnamed-module. But this is temporary and would be removed in future releases and hence is not recommended. Same is the case with unofficial-reflective access. Using “--add-opens” option would temporarily open up the package for reflective access.

Removal of Java EE modules:

The following modules had been marked as deprecated in JDK 9 and have been removed in Java 11.

  • xml.ws (JAX-WS, plus the related technologies SAAJ and Web Services Metadata)
  • xml.bind (JAXB)
  • activation (JAF)
  • xml.ws.annotation (Common Annotations)
  • corba (CORBA)
  • transaction (JTA)

On compiling the application on Java 11, if the code was using one of the above-mentioned functionalities, it would get compilation issues as follows:

Java-8-to-11

Fix:

These modules have been removed from JDK and are now maintained separately and need to be mentioned as dependencies for the application. The issue in the above-mentioned example can be resolved by adding this dependency for JAXB:

Java-8-to-11

Split packages:

Two or more JARs can have the same package name. This is typically the case where one JAR has the interface of the library and other JARs have different implementations. They use the same package name to access the package private classes of each other. This is called split packages.

With Java 9, the split packages in modules are not allowed. Two modules cannot have the same package names, internal or external.

However, one can have split packages in the Unnamed module, that is, multiple JARs mentioned on the class path can still have split packages. But problem arises when a module (with module-info) has the same package name as one of the JARs on the classpath. In this case, following errors are observed:

Java-8-to-11

Fix:

Renaming the package in one of the module/JAR would resolve the issue. If renaming is not possible, we would have to look for alternatives of the conflicting module/JAR.

Modularize your application:

It is not really required to modularize the application for migration. Java 11 supports both classpath and module path, hence all the dependencies can still be mentioned on the classpath. Java internally groups the JARs on the classpath as one module, called the “Unnamed module”.

And if you are looking for a quick migration, you do not need to modularize the application completely.

Following points need to be considered while creating the module-info files:

  • The module name should be unique.
  • All packages that are intended to be used by the outside world need to be mentioned as “exports”. All other packages would be treated as internal.
  • Circular dependencies between modules are not allowed.
  • You cannot have two (or more) modules that export the same package in use at the same time. These are called split packages and are not allowed by the module system.

To summarize, Java 8 is reaching EOL and it is time to move on to the next LTS version of Java, that is Java 11. With Java 11, Oracle JDK is being released with a commercial license and will not be available for free use. There are OpenJDK builds released by other providers, that can be used for free.

Migrating to Java 11 does offer some challenges

  • Unofficial access to JDK internal APIs
  • Unofficial reflective access
  • Removed Java EE modules from JDK
  • Split packages are not allowed.

In conclusion, we can say that there few challenges to migrating to the Java 11 can be easily overcome. For quicker migration, command line directives can be used to allow access to JDK internal APIs, but this is temporary and is not recommended.

For more information or consultation, please contact here.