I tackled down a very specific problem, whose solution seems to be something basic:
My (Spring) application's classloader hierarchy is something like this: SystemClassLoader -> PlatformClassLoader -> AppClassLoader
If I use Java CompleteableFuture
to run threads. the ContextClassLoader
of the threads is: SystemClassLoader -> PlatformClassLoader -> ThreadClassLoader
Thus, I cannot access any class in AppClassLoader
although I have to because all external library classes reside there.
The source base is quite large so I don't want to/can't rewrite all the thread related pieces to something else (e.g. pass a custom executor to each call).
So my question is: How can I make the threads created by e.g. CompleteableFuture.supplyAsync()
use the AppClassLoader
as a parent? (instead of the PlatformClassloader
)
I found out that ForkJoinPool is used to create the threads. But as it seems to me, everything there is static and final. So I doubt that even setting a custom ForkJoinWorkerThreadFactory with a system property will help in this case. Or would it?
Edit to answer the questions from the comments:
where do you deploy to? Is this running within jetty / tomcat / any JEE container?
- I'm using the default Spring Boot setup so an internal tomcat container is used.
What is the exact issue you have?
- The exact issue is: java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
The jobs that you submit to supplyAsync() are created from the AppClassLoader, aren't they?
The
supplyAsync
is called from theMainThread
which uses theAppClassLoader
. But, debugging the applications shows that all such threads havePlatformClassLoader
as their parent. As to my understanding, this happens because ForkJoinPool.commonPool() is constructed during the application startup (because it's static) and so uses the default class loader as the parent which isPlatformClassLoader
. So, all threads from this pool getPlatformClassLoader
as their parent for ContextClassLoader (instead ofAppClassLoader
).When I'm creating my own executor inside the
MainThread
and pass this executor tosupplyAsync
everything works - and I can see during debugging that indeed nowAppClassLoader
is the parent of myThreadClassLoader
. Which seems to affirm my assumption in the first case that the common pool is not created byMainThread
at least not when it's usingAppClassLoader
itself.
Full stacktrace:
java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:851) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:682) ~[na:na]
at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:628) ~[na:na]
at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor$1(Proxy.java:426) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:327) ~[na:na]
at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:203) ~[na:na]
at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:424) ~[na:na]
at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:999) ~[na:na]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:79) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:131) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:93) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
at org.keycloak.admin.client.Keycloak.realms(Keycloak.java:114) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
at org.keycloak.admin.client.Keycloak.realm(Keycloak.java:118) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
supplyAsync()
are created from theAppClassLoader
, aren't they? So they should have access to its classes. This seems like an XY problem to me. – Bowlderkeycloak
– so the corresponding tag might be relevant. – Bowlder