Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OMERO.server: JDK 17 support #6383

Open
sbesson opened this issue Apr 16, 2024 · 1 comment
Open

OMERO.server: JDK 17 support #6383

sbesson opened this issue Apr 16, 2024 · 1 comment
Assignees

Comments

@sbesson
Copy link
Member

sbesson commented Apr 16, 2024

This issue summarizes the current known issues associated with running OMERO.server under a JDK 17 environment and discusses a set of workarounds and proposed changes.

State of Java support

As of OMERO.server 5.6.10, the requirement table of the OMERO documentation 1 defines the current state of Java support. JDK 11 LTS released in September 2018 is the recommendation, JDK 17 LTS released in September 2021 is marked as unsupported and JDK 21 LTS which has been released in September 2023 is not yet captured.

In this table, upstream support for JDK 11 LTS is listed as ending in October 2024 i.e. 6 months from now. The reality is slightly more complex as this timeline largely depends on the OpenJDK build e.g. Oracle JDK 11 is already in extended support 2, the RedHat build of OpenJDK will enter Extended Life Cycle Support in October 3 while Azul Zulu will maintain builds of OpenJDK until 2032 4.

Introducing proper support for Java 17 (and possibly 21) in 2024 would give the community the option either to stay on JDK 11 or upgrade to a more recent JDK LTS version depending on their environment and requirements.

Encapsulation errors and workaround

The bulk of the issues associated with running OMERO.server on JDK 17 are related to the strong encapsulation introduced in Java 16 initially defined in JEP403 5. Various resources are available which describe these changes and their impact on downstream code e.g. 6.

Trying to start OMERO.server-5.6.10-ice36 in a JDK 21 environment on Ubuntu 22.04 results in the following exception

2024-04-16 09:22:33,127 INFO  [                ome.services.blitz.Entry] (      main) Creating OMERO.blitz. Please wait...
2024-04-16 09:22:34,440 WARN  [                 ome.system.OmeroContext] (      main) Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.RawFileStoreSubstituter' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.RawFileStore.xml]: Cannot resolve reference to bean 'readOnlyStatus' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readOnlyStatus' defined in class path resource [ome/services/startup.xml]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [ome.services.util.ReadOnlyStatus]: Constructor threw exception; nested exception is java.lang.ExceptionInInitializerError
2024-04-16 09:22:34,445 ERROR [                ome.services.blitz.Entry] (      main) Error on startup.
org.springframework.beans.factory.access.BootstrapException: Unable to return specified BeanFactory instance: factory key [OMERO.blitz], from group with resource name [classpath*:beanRefContext.xml]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'OMERO.blitz' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-blitz.jar!/beanRefContext.xml]: Cannot resolve reference to bean 'ome.server' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ome.server' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/beanRefContext.xml]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [ome.system.OmeroContext]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.RawFileStoreSubstituter' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.RawFileStore.xml]: Cannot resolve reference to bean 'readOnlyStatus' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readOnlyStatus' defined in class path resource [ome/services/startup.xml]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [ome.services.util.ReadOnlyStatus]: Constructor threw exception; nested exception is java.lang.ExceptionInInitializerError
	at org.springframework.beans.factory.access.SingletonBeanFactoryLocator.useBeanFactory(SingletonBeanFactoryLocator.java:404)
	at ome.system.OmeroContext.getInstance(OmeroContext.java:202)
	at ome.services.blitz.Entry.start(Entry.java:189)
	at ome.services.blitz.Entry.main(Entry.java:146)
...
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @fdc8126
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
	at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:159)
	at javassist.util.proxy.DefineClassHelper$JavaOther.defineClass(DefineClassHelper.java:213)
	at javassist.util.proxy.DefineClassHelper$Java11.defineClass(DefineClassHelper.java:52)
	at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260)
	at javassist.ClassPool.toClass(ClassPool.java:1232)
	at javassist.CtClass.toClass(CtClass.java:1384)
	at bitronix.tm.resource.jdbc.proxy.JdbcJavassistProxyFactory.generateProxyClass(JdbcJavassistProxyFactory.java:265)
	at bitronix.tm.resource.jdbc.proxy.JdbcJavassistProxyFactory.createProxyConnectionClass(JdbcJavassistProxyFactory.java:156)
	... 110 common frames omitted

After setting omero.jvmcfg.append to --add-opens java.base/java.lang=ALL-UNNAMED and restarting the server

2024-04-16 09:25:03,635 ERROR [                ome.services.blitz.Entry] (      main) Error on startup.
org.springframework.beans.factory.access.BootstrapException: Unable to return specified BeanFactory instance: factory key [OMERO.blitz], from group with resource name [classpath*:beanRefContext.xml]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'OMERO.blitz' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-blitz.jar!/beanRefContext.xml]: Cannot resolve reference to bean 'ome.server' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ome.server' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/beanRefContext.xml]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [ome.system.OmeroContext]: Constructor threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ome.util.MapPut#0' defined in class path resource [ome/services/hibernate.xml]: Cannot resolve reference to bean 'fullTextIndexer' while setting bean property 'object'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fullTextIndexer' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.Search.xml]: Cannot resolve reference to bean 'eventLogLoader' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'eventLogQueue' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.Search.xml]: Cannot resolve reference to bean 'internal-ome.api.ITypes' while setting bean property 'types'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.ITypes' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.ITypes.xml]: Cannot resolve reference to bean 'internal-ome.api.IUpdate' while setting bean property 'updateService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.IUpdate' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.IUpdate.xml]: Cannot resolve reference to bean 'internal-ome.api.LocalAdmin' while setting bean property 'adminService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.IAdmin' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.IAdmin.xml]: Cannot resolve reference to bean 'passwordProvider' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'chainedPasswordProvider' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.IAdmin.xml]: Cannot resolve reference to bean 'ldapPasswordProvider' while setting constructor argument with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ldapPasswordProvider' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.IAdmin.xml]: Cannot resolve reference to bean 'internal-ome.api.ILdap' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'internal-ome.api.ILdap' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.ILdap.xml]: Cannot resolve reference to bean 'contextSource' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contextSource' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.ILdap.xml]: Cannot resolve reference to bean 'contextSourceSwapper' while setting bean property 'targetSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contextSourceSwapper' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.ILdap.xml]: Cannot resolve reference to bean 'defaultContextSource' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultContextSource' defined in URL [jar:file:/opt/omero/OMERO.server-5.6.10-ice36/lib/server/omero-server.jar!/ome/services/service-ome.api.ILdap.xml]: Bean instantiation via constructor failed; nested exception is java.lang.IllegalAccessError: class org.springframework.ldap.core.support.AbstractContextSource (in unnamed module @0x161b062a) cannot access class com.sun.jndi.ldap.LdapCtxFactory (in module java.naming) because module java.naming does not export com.sun.jndi.ldap to unnamed module @0x161b062a
	at org.springframework.beans.factory.access.SingletonBeanFactoryLocator.useBeanFactory(SingletonBeanFactoryLocator.java:404)
	at ome.system.OmeroContext.getInstance(OmeroContext.java:202)
	at ome.services.blitz.Entry.start(Entry.java:189)
	at ome.services.blitz.Entry.main(Entry.java:146)
...
Caused by: java.lang.IllegalAccessError: class org.springframework.ldap.core.support.AbstractContextSource (in unnamed module @0x161b062a) cannot access class com.sun.jndi.ldap.LdapCtxFactory (in module java.naming) because module java.naming does not export com.sun.jndi.ldap to unnamed module @0x161b062a
	at org.springframework.ldap.core.support.AbstractContextSource.<clinit>(AbstractContextSource.java:77)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:142)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:271)
	... 171 common frames omitted

Setting omero.jvmcfg.append to --add-opens java.base/java.lang=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.ldap=ALL-UNNAMED is sufficient to start OMERO.server and perform some minimal workflows: login, import, view, delete...

Minimal testing also indicated the changes above are sufficient to start OMERO.server in a JDK 21 LTS environment.

Dependency upgrades

While the paragraph above describes configuration workarounds that can be used immediately to deploy OMERO.server in a JDK 17 environment, a more robust approach would be to update the underlying libraries to versions that are not relying on features now prevented by the upstream Java changes.

Bitronix Transaction Manager

Bitronix Transaction Manager (btm) is a direct dependency of the omero-server component 7. This library is an implementation of the Java Transaction API 1.1 (JTA). Up to OMERO.server 5.4.x, the dependency was using org.codehaus.btm:btm. In OMERO 5.5.0 and during the transition to decoupled components & the Gradle build system, this dependency was changed to com.github.marcus-nl.btm:btm:3.0.0-mk1.

The latter artifact comes from a fork of https://github.com/scalar-labs/btm which was inactive at the time but it looks like recent activity has started again on this repository. The state of these different existing implementations should be reviewed to assess whether we could upgrade this dependency to a recent version compatible with the JDK encapsulation changes.

Spring LDAP core

spring-ldap-core is a transitive dependency of the omero-server component through org.springframework.security:spring-security-ldap 8. The usage of internal com.sun.jndi.ldap classes was fixed upstream in 2.3.4.RELEASE 9. Upgrading manually spring-ldap-core to this version and reverting the --add-exports java.naming/com.sun.jndi.ldap=ALL-UNNAMED from the configuration above was sufficient to start the server.

At minimum, it should be possible to upgrade org.springframework.security:spring-ldap-core but this is probably a good opportunity to upgrade spring-security-ldap and review the state of the entire set of Spring dependencies as we have a mixture of versions.

/cc @jburel @pwalczysko @khaledk2 @dominikl @chris-allan @kkoz @joshmoore

Footnotes

  1. https://omero.readthedocs.io/en/v5.6.10/sysadmins/version-requirements.html#id5

  2. https://www.oracle.com/uk/java/technologies/java-se-support-roadmap.html

  3. https://access.redhat.com/articles/1299013#OpenJDK_Life_Cycle

  4. https://www.azul.com/products/azul-support-roadmap/

  5. https://openjdk.org/jeps/403

  6. https://blogs.oracle.com/javamagazine/post/a-peek-into-java-17-continuing-the-drive-to-encapsulate-the-java-runtime-internals

  7. https://github.com/ome/omero-server/blob/f67fdb63410655c6d5a5fc5e566149479bfaa8bc/build.gradle#L48

  8. https://github.com/ome/omero-server/blob/f67fdb63410655c6d5a5fc5e566149479bfaa8bc/build.gradle#L36

  9. https://github.com/spring-projects/spring-ldap/releases/tag/2.3.4.RELEASE

@sbesson sbesson self-assigned this Apr 16, 2024
@jburel
Copy link
Member

jburel commented May 2, 2024

As discussed on 30/04

  • Update documentation with workaround to allow the server to start and run on Java 17+
  • Adjust CI infrastructure to test the the workaround at regular intervals See Java option devspace#217

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants