Complete FutureJoinPool Set Loader

I considered a very specific problem, the solution of which seems to be something simple:

My (Spring) hierarchy of application class loaders looks something like this: SystemClassLoader -> PlatformClassLoader -> AppClassLoader

If I use Java CompleteableFutureto run threads. ContextClassLoaderthreads:SystemClassLoader -> PlatformClassLoader -> ThreadClassLoader

Thus, I cannot access any class in AppClassLoader, although I must, because all the classes of external libraries are there.

The source database is quite large, so I don’t want / cannot rewrite all the parts associated with the flows into something else (for example, transfer my own executor to each call).

So my question is: How can I create themes created, for example, to CompleteableFuture.supplyAsync()use AppClassLoaderas a parent? (instead PlatformClassloader)

I found that ForkJoinPool is used to create threads. But, as it seems to me, everything is static and final. Therefore, I doubt that even setting a custom ForkJoinWorkerThreadFactory with a system property will help in this case. Or will it be?

Edit to answer questions from comments:

  • where do you deploy? Does it work in Jetty / Tomcat / any JEE container?

    • I use the default Spring Boot setting, so the Tomcat internal container is used.
  • What is your specific problem?

    • Exact problem: java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource the method refers to is not visible in the class loader
  • Jobs that you submit to supplyAsync () are created from AppClassLoader, right?

    • supplyAsync MainThread, AppClassLoader. , PlatformClassLoader . , , ForkJoinPool.commonPool() ( ) , PlatformClassLoader. , PlatformClassLoader ContextClassLoader ( AppClassLoader).

    • MainThread supplyAsync, - , AppClassLoader ThreadClassLoader. , -, , MainThread, , AppClassLoader.

:

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]
+12
3

, , , , :

, ForkJoinPool.commonPool(). commonPool ( , ) . API Java.

    • ( Spring Boot) ApplicationReadyEvent
    • , ,

      @Component
      class ForkJoinCommonPoolFix : ApplicationListener<ApplicationReadyEvent> {
          override fun onApplicationEvent(event: ApplicationReadyEvent?) {
        }
      }
      
  1. ForkJoinWorkerThreadFactory commonPool ( )

    • val javaClass = ForkJoinPool.commonPool()::class.java
      val field = javaClass.getDeclaredField("factory")
      field.isAccessible = true
      val modifiers = field::class.java.getDeclaredField("modifiers")
      modifiers.isAccessible = true
      modifiers.setInt(field, field.modifiers and Modifier.FINAL.inv())
      field.set(ForkJoinPool.commonPool(), CustomForkJoinWorkerThreadFactory())
      field.isAccessible = false
      
  2. CustomForkJoinWorkerThreadFactory

    • //Custom class
      class CustomForkJoinWorkerThreadFactory : ForkJoinPool.ForkJoinWorkerThreadFactory {
        override fun newThread(pool: ForkJoinPool?): ForkJoinWorkerThread {
          return CustomForkJoinWorkerThread(pool)
        }
      }
      // helper class (probably only needed in kotlin)
      class CustomForkJoinWorkerThread(pool: ForkJoinPool?) : ForkJoinWorkerThread(pool)
      

, , . . : - , .

: . . . ( !).

:

: ( ), . :

ExecutorService pool = Executors.newFixedThreadPool(10);
final CompletableFuture<String> future = 
    CompletableFuture.supplyAsync(() -> { /* ... */ }, pool);
+5

, resteasy lib : http://grepcode.com/file/repo1.maven.org/maven2/org.jboss.resteasy/resteasy-client/3.0-beta-1/org/jboss/resteasy/client/jaxrs/ProxyBuilder.java#21.

resteasy , , , , , , .

: ThreadClassLoader , , AppClassLoader , ThreadClassLoader (ThreadClassLoader AppClassLoader).

, ClassLoader ClassLoader: thread.setContextClassLoader(appClass.class.getClassLoader())

+3

- , , , JDK9-JDK11.

Javadocs :

, , :

  • java.util.concurrent.ForkJoinPool.common.threadFactory - ForkJoinPool.ForkJoinWorkerThreadFactory. .

, ForkJoinWorkerThreadFactory ClassLoader , .

ForkJoinWorkerThreadFactory:

package foo;

public class MyForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory {

    @Override
    public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return new MyForkJoinWorkerThread(pool);
    }

    private static class MyForkJoinWorkerThread extends ForkJoinWorkerThread {

        private MyForkJoinWorkerThread(final ForkJoinPool pool) {
            super(pool);
            // set the correct classloader here
            setContextClassLoader(Thread.currentThread().getContextClassLoader());
        }
    }
} 

-Djava.util.concurrent.ForkJoinPool.common.threadFactory=foo.MyForkJoinWorkerThreadFactory

, , ForkJoinPool commonPool, ClassLoader , ( System).

, :

Fork/Join .

Java SE 9 , fork/join, . , fork/join, , . , fork/join, , .

As a result of the above change of backward incompatibility, things that use ForkJoinPoolthat worked in JDK8 may not work in JDK9 +.

0
source

Source: https://habr.com/ru/post/1694483/


All Articles