CompletableFuture / ForkJoinPool Set Class Loader
Abordé un problema muy específico, cuya solución parece ser algo básico:
La jerarquía del cargador de clases de mi aplicación (Spring) es algo como esto:SystemClassLoader -> PlatformClassLoader -> AppClassLoader
Si uso JavaCompleteableFuture
para ejecutar hilos. elContextClassLoader
de los hilos es:SystemClassLoader -> PlatformClassLoader -> ThreadClassLoader
Por lo tanto, no puedo acceder a ninguna clase enAppClassLoader
aunque tengo que hacerlo porque todas las clases de bibliotecas externas residen allí.
La base de origen es bastante grande, por lo que no quiero / no puedo reescribir todas las piezas relacionadas con el hilo a otra cosa (por ejemplo, pasar un ejecutor personalizado a cada llamada).
Entonces mi pregunta es:¿Cómo puedo hacer los hilos creados por ej.CompleteableFuture.supplyAsync()
utilizar elAppClassLoader
¿como un padre? (en vez dePlatformClassloader
)
Descubrí queForkJoinPool se usa para crear los hilos. Pero como me parece, todo lo que hayestático yfinal. Así que dudo que incluso estableciendo una costumbreForkJoinWorkerThreadFactory con una propiedad del sistema ayudará en este caso. O lo haría?
Edite para responder las preguntas de los comentarios:
¿A dónde se despliegan? ¿Se está ejecutando dentro de jetty / tomcat / any JEE container?
Estoy usando la configuración predeterminada de Spring Boot, por lo que se usa un contenedor interno tomcat.¿Cuál es el problema exacto que tienes?
El problema exacto es:java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenciado desde un método no es visible desde el cargador de clasesLos trabajos que envía a supplyAsync () se crean desde AppClassLoader, ¿no es así?
lossupplyAsync
se llama desde elMainThread
que usa elAppClassLoader
. Pero, la depuración de las aplicaciones muestra que todos estos hilos tienenPlatformClassLoader
como sus padres En mi opinión, esto sucede porqueForkJoinPool.commonPool () se construye durante el inicio de la aplicación (porque es estático) y, por lo tanto, utiliza el cargador de clases predeterminado como padre, que esPlatformClassLoader
. Entonces, todos los hilos de este grupo obtienenPlatformClassLoader
como sus padres paraContextClassLoader (en lugar deAppClassLoader
)
Cuando estoy creando mi propio ejecutor dentro delMainThread
y pasar este ejecutor asupplyAsync
todo funciona, y puedo ver durante la depuración que de hecho ahoraAppClassLoader
es el padre de miThreadClassLoader
. Lo que parece afirmar mi suposición en el primer caso de que el grupo común no es creado porMainThread
al menos no cuando está usandoAppClassLoader
sí mismo.
Traza completa de la pila:
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]