JDK-17/JDK-21: Solving The Error module does not opens to unnamed module...

When using your own setenv.sh/setenv.bat scripts to set your Tomcat environment, be sure to update the Java options...

JDK-17/JDK-21 Opens Error

Just a quick one today that will hopefully save you some time if you hit this yourself...

I'm updating a workspace to use JDK-21 (it was previously working fine w/ JDK-11).

When I started Tomcat, I immediately got an error:

10-Oct-2024 15:36:08.783 SEVERE [main] org.apache.catalina.startup.HostConfig.
    deployDescriptor Error deploying deployment descriptor 
    [tomcat/conf/Catalina/localhost/ROOT.xml]
  java.lang.IllegalStateException: Error starting child
    [snip]
  Caused by: org.apache.catalina.LifecycleException: Failed to start component 
      [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
    [snip]
  Caused by: java.lang.ExceptionInInitializerError
    at com.liferay.petra.reflect.ReflectionUtil.(ReflectionUtil.java:125)
    at com.liferay.portal.kernel.util.ProxyUtil.(ProxyUtil.java:200)
    [snip]
  Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field 
      static final java.lang.invoke.MethodHandles$Lookup 
      java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP accessible: module 
      java.base does not "opens java.lang.invoke" to unnamed module @2f860823
    [snip]
10-Oct-2024 15:36:08.798 INFO [main] org.apache.catalina.startup.Catalina.start 
    Server startup in [241] milliseconds

The key here, and the one that I hope Google is indexing and that's how you got here, is this exception:

java.lang.reflect.InaccessibleObjectException: Unable to make field static final java.lang.invoke.MethodHandles$Lookup java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP accessible: module java.base does not "opens java.lang.invoke" to unnamed module

When you get this message, it's a sign that your JDK Java options are too restrictive.

In my case, I had an existing setenv.sh file that I had pulled forward and was using. It contained the one line:

JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED"

Remember that old fix for JDK 11.0.20+ that we needed to fix zip file access? Well, that's what this line was for.

However, for JDK 17 and JDK 21, Liferay needs a few more additions... The complete list is:

  • --add-opens=java.base/java.lang=ALL-UNNAMED
  • --add-opens=java.base/java.lang.invoke=ALL-UNNAMED
  • --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
  • --add-opens=java.base/java.net=ALL-UNNAMED
  • --add-opens=java.base/sun.net.www.protocol.http=ALL-UNNAMED
  • --add-opens=java.base/sun.net.www.protocol.https=ALL-UNNAMED
  • --add-opens=java.base/sun.util.calendar=ALL-UNNAMED
  • --add-opens=jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED

When you add these to your setenv.sh/setenv.bat file, those exceptions above will fall away.

Now it is quite possible that, due to your own customizations, you might encounter a similar exception...

When that happens, you're going to need to adjust your --add-opens list to include your referenced module, and have it access ALL-UNNAMED.

But how do you do it? Mostly it's going to come from the exception message itself.

We already know we're going to need an --add-opens argument, and it will equal the module which is being flagged and the "opens package" values to complete the line, and then we set it equal to ALL-UNNAMED.

So from my original exception above, the portion of the message I have to look at is module java.base does not "opens java.lang.invoke". I'm going to extract the relevant pieces to come up with --add-opens=java.base/java.lang.invoke=ALL-UNNAMED.

Hope this helps as you migrate to JDK-17 or JDK-21...

Blogs

Thanks, Dave.

Regarding the complete list, a good source of the required arguments is the default setenv.sh file we use: https://github.com/liferay/liferay-portal/blob/7.4.3.127-ga127/tools/servers/tomcat/bin/setenv.sh#L3

HTH.

Can I please make the suggestion that perhaps the customer could add their own 'setenv-ext.sh' file, and the Liferay default setenv.sh checks for that file's existence and calls it automatically?

I have needed to edit the setenv.sh file in Tomcat for years, and especially with the 7.4 and Quarterly Releases being 'full deploys and copy across your files' - the upgrade now takes a lot of steps. If we could make it easier to deploy an upgrade that would be great.

Many thanks, Tony

Hey David,

thanks for this article. Although some error messages are gone after what you suggest, I still experience some issues. In particular, the main exception I get is this one: Caused by: java.lang.NoSuchFieldException: modifiers     at java.base/java.lang.Class.getDeclaredField(Class.java:2782)     at com.liferay.petra.reflect.ReflectionUtil.<clinit>(ReflectionUtil.java:157)     ... 49 more

I run on DXP 7.4, with OpenJDK 21. I've used all the --add-opens you've suggested. Any idea on what could be the cause of the error or on how to solve it?

What version of 7.4? Because JDK 21 compatibility is only available in later quarterly releases, it's not going to work across all of 7.4 releases.

Oh, didn't know about this. The version I am using is the 7.4.3.73 CE GA73, Build 7403. Is there some page where I can find the compatilibity, version by version?