JDK-8223260 has been recently been fixed in jdk 11. When using a JDK that includes this fix, code relying on javax.naming will encounter exceptions like this:
2020-04-17 15:21:30,378 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /api/requiresStandardRole failed, error id: 4af2a4af-1daf-448c-8336-69ab4abec3c2-1: com.oracle.svm.core.jdk.UnsupportedFeatureError: JDK11OrLater: Target_java_lang_ClassLoader.createOrGetClassLoaderValueMap()
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:86)
at java.lang.ClassLoader.createOrGetClassLoaderValueMap(ClassLoader.java:254)
at java.lang.System$2.createOrGetClassLoaderValueMap(System.java:2125)
at jdk.internal.loader.AbstractClassLoaderValue.map(AbstractClassLoaderValue.java:266)
at jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:189)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:711)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
at javax.naming.InitialContext.init(InitialContext.java:236)
at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:154)
This is due to a cache introduced in the JDK.
I've found a workaround for Quarkus (see PR associated with https://github.com/quarkusio/quarkus/issues/8651), but others are likely to encounter similar issues. So, we should probably have a general solution for all.
Unfortunately the caching code (see here) is embedded right into the getInitialContext method which makes does not make it easy to substitute for.
At first glance, I can't see a right substitution that could be made. I wonder if the prohibition of createOrGetClassLoaderValueMap is too harsh? What's the issue with it? Creating a CHM of ClassLoaders? Or the reflection call inside createOrGetClassLoaderValueMap?
The change that introduced this prohibition was 316703f4a3fc9d9b9c0b0e448da48f3b2d0131b8. One way to solve this would be for AbstractClassLoaderValue.map to assume that classloader is always null, assuming actual classloader parameters are ignored?
Simple reproducer for this:
import java.util.Hashtable;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.spi.NamingManager;
public class InitialContextTest {
public static void main(String[] args) {
try {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
NamingManager.getInitialContext(env);
} catch (CommunicationException e) {
System.out.println("Test passed");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("Test failed");
}
}
}
$ ./svmbuild/vm/bin/javac InitialContextTest.java
$ ./svmbuild/vm/native-image InitialContextTest testme-init-ctx
[testme-init-ctx:186225] classlist: 2,051.75 ms, 0.96 GB
[testme-init-ctx:186225] (cap): 764.54 ms, 0.96 GB
[testme-init-ctx:186225] setup: 2,556.28 ms, 0.96 GB
[testme-init-ctx:186225] (clinit): 317.95 ms, 1.71 GB
[testme-init-ctx:186225] (typeflow): 6,213.71 ms, 1.71 GB
[testme-init-ctx:186225] (objects): 8,116.09 ms, 1.71 GB
[testme-init-ctx:186225] (features): 349.50 ms, 1.71 GB
[testme-init-ctx:186225] analysis: 15,311.77 ms, 1.71 GB
[testme-init-ctx:186225] universe: 431.44 ms, 1.71 GB
[testme-init-ctx:186225] (parse): 1,537.62 ms, 1.71 GB
[testme-init-ctx:186225] (inline): 1,682.40 ms, 2.23 GB
[testme-init-ctx:186225] (compile): 10,572.44 ms, 3.16 GB
[testme-init-ctx:186225] compile: 14,545.77 ms, 3.16 GB
[testme-init-ctx:186225] image: 1,572.62 ms, 3.16 GB
[testme-init-ctx:186225] write: 280.81 ms, 3.16 GB
[testme-init-ctx:186225] [total]: 37,072.53 ms, 3.16 GB
$ ./testme-init-ctx
com.oracle.svm.core.jdk.UnsupportedFeatureError: JDK11OrLater: Target_java_lang_ClassLoader.createOrGetClassLoaderValueMap()
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:86)
at java.lang.ClassLoader.createOrGetClassLoaderValueMap(ClassLoader.java:254)
at java.lang.System$2.createOrGetClassLoaderValueMap(System.java:2128)
at jdk.internal.loader.AbstractClassLoaderValue.map(AbstractClassLoaderValue.java:266)
at jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:189)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:711)
at InitialContextTest.main(InitialContextTest.java:13)
Test failed
Expected (e.g in JVM mode):
Test passed
@galderz thanks for the heads-up. @jerboaa thanks for the reproducer. We will look into it.
@olpaw Appreciate that. Thanks!
@galderz with my changes on https://github.com/oracle/graal/pull/2409 I can get the reproducer from @jerboaa working. (It just needs a reflection config for the com.sun.jndi.ldap.LdapCtxFactory default constructor. But that's to be expected.)
The question is if the fix is also sufficient for the quarkus usecase. Please give it a try.
@olpaw Thx for the fix. I'll give it a go shortly...
@galderz If you don't want to build a complete GraalVM release for testing you can use:
graal/vm> mx --dy /substratevm --disable-installables=true --force-bash-launchers=gu,native-image-configure,polyglot --skip-libraries=native-image-agent,polyglot
graal/vm> export JAVA_HOME=$PWD/latest_graalvm_home && export PATH=$JAVA_HOME/bin:$PATH
@olpaw I tested your branch with Quarkus and it worked fine.
The fix is on master and also on the 20.1 branch.