Moving Workspaces to JDK 11

We're finally getting close to official word that JDK 11 is fully supported. These are steps you can take in advance.

Introduction

As of yet, there has been no official word that JDK 11 is supported by the Liferay developer tools. Just like the old delays going from JDK 7 to JDK 8, various third party tools and libraries held up the switch to JDK 11.

Up until now, some developers had luck using JDK 11, but there were lingering issues with some tools such as Service Builder which could get in the way.

In fact, my general recommendation has been to just stay with JDK 8 until the dev tools were all declared compatible with JDK 11. And that day is just around the corner, so let's see what will be necessary for using JDK 11 on an existing workspace.

One note here: It is still going to be okay to continue development under JDK 8 and deploy to a Liferay environment running under JDK 11. That will be supported for some time to come yet.
Another note here: If you compile under JDK 11, you must run under JDK 11. If your app servers are running on JDK 8, they should be updated to use JDK 11. I'd recommend doing that first so you know your environment is ready for JDK 11 before tackling your workspace changes.

I have a current Liferay 7.2 workspace that contains some themes and some modules, everything currently works fine under a JDK 8 build and it is about to be moved to a JDK 11 build.

There's two basic steps that need to be taken.

  1. Update the Gradle version to at least 6.6.1
  2. Update the Liferay Workspace plugin to at least 3.4.2
  3. Update Service Builder -service module build.gradle files

Before making any changes, I made sure that I had some kind of backup. My workspace is in Git, so I'm good; I even made a custom branch so I could isolate my changes for JDK 11. If you're not using revision control (yet), a tarball of the workspace is a decent alternative.

I also cleaned out all legacy build artifacts. A gradlew clean command will do it, I used git clean -fdx as an alternative.

The goal here is to clean any remnants of a JDK 8 build. We want to ensure that we will be using JDK 11 fully after we apply these updates.

Updating Gradle

This is pretty easy. In the root of our workspace, we need to edit the gradle/wrapper/gradle-wrapper.properties file.

Change the value for the distributionUrl so it refers to 6.6.1 instead of what it currently says (unless you're at a later version already). My file now reads:

#Thu Jun 11 16:44:41 EDT 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

With this change done, on the next gradlew command you enter, the 6.6.1 version will be downloaded and used for the build going forward.

I tried issuing a simple command, gradlew tasks, and it downloaded 6.6.1, but then I got a subsequent error:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':modules:channels-servlet'.
> Failed to apply plugin class 'com.liferay.gradle.plugins.node.NodePlugin'.
   > Could not create plugin of type 'NodePlugin'.
      > Could not generate a decorated class for type NodePlugin.
         > org/gradle/api/internal/plugins/osgi/OsgiHelper

Since I'm only part way through the necessary changes, I wasn't surprised by the error and new I needed to go on to the next step.

Updating the Liferay Workspace Plugin

This is pretty easy too. In the root of our workspace, we need to edit the settings.gradle file to change the plugin version.

Change the version for the com.liferay.gradle.plugins.workspace to 3.4.2 unless you already have a later version. My file now reads:

buildscript {
  dependencies {
    classpath group: "com.liferay", name: "com.liferay.gradle.plugins.workspace", version: "3.4.2"
    classpath group: "net.saliman", name: "gradle-properties-plugin", version: "1.4.6"
  }

  repositories {
    maven {
      url "https://repository-cdn.liferay.com/nexus/content/groups/public"
    }
  }
}

apply plugin: "net.saliman.properties"

apply plugin: "com.liferay.workspace"

Now I've implemented all of the changes, but I still need to test it out.

ServiceBuilder -service Module build.gradle Updates

If you have an older workspace, the build.gradle file in the -service module for SB need an addition. Add the following stanza to the file:

tasks.withType(JavaCompile) {

	// Generated classes using Jodd library are unable to be read when compiled against JDK 11

	sourceCompatibility = JavaVersion.VERSION_1_8
	targetCompatibility = JavaVersion.VERSION_1_8
}

This is only necessary in the -service module, you don't need to add this to the -api module or any other.

Update: IllegalArgumentException from Jodd...

So today I needed to recompile a workspace under JDK-11. I changed my JDK and did a gw clean build to ensure everything was ready for 11. No compile errors, so I was good to go.

Deployed this to my Liferay 7.2 bundle and started it up under JDK-11 and started my testing.

My remote SB services were failing. No idea why until I ramped up debug logging on com.liferay.portal.jsonwebservice package, then I started to see that I was, for some unknown reason, getting IllegalArgumentExceptions stemming from Jodd, a package Liferay uses for reflection help.

Since I saw Jodd in the stack trace (sorry, I didn't keep it to add here), I figured it was going to be a java version issue, but it was actually a bit trickier...

Some of my remote SB services were working fine, but not all of them. Any service method which included a ServiceContext parameter was failing.

I checked my build.gradle file for the -service module and, sure enough, it didn't have the snippet above to force JDK-8 for compile. I added it and tried to compile, but it complained because the -api module was compiled under 11 and it didn't like the mismatch. I added the JDK-8 snippet to the build.gradle for the -api module and both were happy again.

So still using JDK-11, my -service and -api modules are targeting JDK-8 via the snippet above, when these modules are deployed to Liferay running under JDK-11, the services started working again.

So if you're testing something and you get a stack trace and you notice Jodd is seemingly at the source of the exception, you're going to need to add the above snippet so the code will be JDK-8 compatible; that should fix up your runtime.

Test, Test, Test

Yes, we want to test the builds, Service Builder if we're using it, Theme building, the whole nine yards.

Missing liferay.workspace.product

The first thing I noticed is a message notifying me that I don't have the property liferay.workspace.product set in gradle.properties. I didn't need that property before, so I had no reason to have it.

Since my workspace is for Liferay 7.2 DXP, I added the following line to my gradle.properties file:

liferay.workspace.product=dxp-7.2-sp3

I got this value using the blade init -l command to list valid versions. If the one you are using is missing, try blade init -L to get the full list of supported values.

With this set, the specific warning went away, but as I had other properties set in gradle.properties, I received a number of notes that I was overriding values from the workspace product value. I solved this by commenting out those other properties.

ReflectionUtil Illegal Reflective Access

Next, for (re-)building the services, I received the following error:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.liferay.petra.reflect.ReflectionUtil 
  (file:.gradle/caches/modules-2/files-2.1/com.liferay/
  com.liferay.portal.tools.service.builder/1.0.375/
  3b92444a4554540ab05e8ce15925331b32ece9dc/
  com.liferay.portal.tools.service.builder-1.0.375.jar) to method java.lang.Object.clone()
WARNING: Please consider reporting this to the maintainers of 
  com.liferay.petra.reflect.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal 
  reflective access operations
WARNING: All illegal access operations will be denied in a future release

I checked with Liferay and found that this error started after the release of JDK 9 and at this point it is only a warning. Support for this may be deprecated in a future JDK release, but it is not an immediate concern. This warning is a warning only, and will not affect the build.

You'll also see this message when you start the bundle under JDK 11, you can safely ignore that one too.

Removal of JAX-WS

Next I ran into a JDK 11 issue. One of my modules includes javax.xml.soap classes and those were removed finally from JDK 11. Well, it was removed from the JDK, but not removed as a dependency in my module. I solved this by adding compile 'com.sun.xml.ws:jaxws-ri:2.3.2' as a dependency in the build.gradle file.

The rest of my build, including themes and other modules, worked without issue.

Deployment

To prepare my bundle for JDK 11, I deleted the osgi/state directory. No one told me this was necessary, I just did it to ensure I didn't have any serialized classes there that a different JDK might have problems with.

During startup and shutdown using the shell scripts, I did see a new message:

NOTE: Picked up JDK_JAVA_OPTIONS:
  --add-opens=java.base/java.lang=ALL-UNNAMED
  --add-opens=java.base/java.io=ALL-UNNAMED 
  --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED

This is an artifact of using JDKs 10 or later with Tomcat. Tomcat sets these values in the catalina.sh script, and the JVM will log that they are used. Tomcat sets these to maintain functionality under the newer JVMs. According to the Tomcat mailing lists, this is what it is and they offer no way to remove this note.

Otherwise deployment and testing went very well. I exercised each portlet, checked for each modification, verified that all was working well.

Conclusion

Following these steps I was able to get my environment to build under JDK 11.

Give these instructions a try in your environment and, if you have any problems, let us know.

And remember, if you're totally stopped from building, you should still have that backup we took earlier so you can revert back to JDK 8 until the problems can be resolved.

Update 1

Daniel Yee from the community and reached out with a problem he faced when trying to update to JDK 11:

I tried updating my workspace and got a handshake error. The gradle wrapper downloaded correctly, but after changing the workspace version, I get the error.Reverting back to the previous Gradle and workspace versions allows it to work again.Have you run into this issue before?

Could not resolve all artifacts for configuration 'classpath'.
Could not resolve com.liferay:com.liferay.gradle.plugins.workspace:3.4.2.
  Required by:
      unspecified:unspecified:unspecified
   > Could not resolve com.liferay:com.liferay.gradle.plugins.workspace:3.4.2.
      > Could not get resource '<a target="_blank" data-stringify-link="https://repository-cdn.liferay.com/nexus/content/groups/public/com/liferay/com.liferay.gradle.plugins.workspace/3.4.2/com.liferay.gradle.plugins.workspace-3.4.2.pom" 
        delay="150" data-sk="tooltip_parent" rel="noopener noreferrer" 
        href="https://repository-cdn.liferay.com/nexus/content/groups/public/com/liferay/com.liferay.gradle.plugins.workspace/3.4.2/com.liferay.gradle.plugins.workspace-3.4.2.pom">
          https://repository-cdn.liferay.com/nexus/content/groups/public/com/liferay/com.liferay.gradle.plugins.workspace/3.4.2/com.liferay.gradle.plugins.workspace-3.4.2.pom</a>'.

This ended up being a CDN issue. By adding the following repository reference to the settings.gradle file, the artifacts were able to resolve:

maven {
	url "http://repository.liferay.com/nexus/content/groups/public"
}

Update 2

I added a section above talking about an IllegalArgumentException I was getting with Jodd. Basically if you see Jodd in your stack trace, you're likely going to need to add the JDK-8 compatibility snippet to your build.gradle to make the exceptions go away.

Blogs

Hey David,

Such an apt time for this useful post! Thank you so much. That's informative. I have a few questions for you.

I was working creating a new workspace on JDK 11. I was able to get everything working, except service builder.

* What went wrong:

Could not determine the dependencies of task ':modules:employee:employee-service:compileJava'.

> Could not resolve all task dependencies for configuration ':modules:employee:employee-service:compileClasspath'.

   > Could not resolve project :modules:employee:employee-api.

     Required by:

         project :modules:employee:employee-service

      > No matching variant of project :modules:employee:employee-api was found. The consumer was configured to find an API of a library compatible with Java 8, preferably in the form of class files, and its dependencies declared externally but:

          - Variant 'apiElements' capability LRPortal.modules.employee:employee-api:1.0.0 declares an API of a library, packaged as a jar, and its dependencies declared externally:

              - Incompatible because this component declares a component compatible with Java 11 and the consumer needed a component compatible with Java 8

          - Variant 'runtimeElements' capability LRPortal.modules.employee:employee-api:1.0.0 declares a runtime of a library, packaged as a jar, and its dependencies declared externally:

              - Incompatible because this component declares a component compatible with Java 11 and the consumer needed a component compatible with Java 8

 

Did you get this too? I end up getting the above when i try to do a gradle deploy. Not sure what else needs to be fixed here. Build.properties was having by default entries for source and target compatibility for 1_8

 

Thanks,

Shipra

 

 

 

 

Sorry, Shipra, I haven't seen this one before. Can you post to the #portal channel on the community slack? We can discuss there and figure out what might be happening and how to resolve, then I can update the blog with the outcome...

A little late, but for all to be aware:

This was likely caused by the ":modules:employee:employee-api" module being compiled to 11, where " :modules:employee:employee-service" was compiling to 1.8.

I hit this too, but in a more convoluted way (with two separate Workspaces, where one WS (compiled to 1.8) was using modules from the other Workspace (compiled to 11), and shared via Maven Local).

Hi David,

I've been playing with Headless and Java 11. I've realized that the standard build process is not working due to removed classes, such as:   

javax.annotation.Generated javax.xml.bind.annotation.XmlRootElement  

Plus missing GraphQL references.

I was wondering if you have an example for Java 11, I can reference or point me to some documentation.