title: CompletableFuture / ForkJoinPool Set Class Loader
body: <p>I tackled down a very specific problem, whose solution seems to be something basic:</p>
  
  <p>My (Spring) application's classloader hierarchy is something like this: <code>SystemClassLoader -&gt; PlatformClassLoader -&gt; AppClassLoader</code></p>
  
  <p>If I use Java <code>CompleteableFuture</code> to run threads. the <code>ContextClassLoader</code> of the threads is:  <code>SystemClassLoader -&gt; PlatformClassLoader -&gt; ThreadClassLoader</code></p>
  
  <p>Thus, I cannot access any class in <code>AppClassLoader</code> although I have to because all external library classes reside there.</p>
  
  <p>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).</p>
  
  <p>So my question is: <strong>How can I make the threads created by e.g. <code>CompleteableFuture.supplyAsync()</code> use the <code>AppClassLoader</code> as a parent?</strong> (instead of the <code>PlatformClassloader</code>)</p>
  
  <p>I found out that <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html" rel="noreferrer">ForkJoinPool</a> is used to create the threads. But as it seems to me, everything there is <em>static</em> and <em>final</em>. So I doubt that even setting a custom <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.ForkJoinWorkerThreadFactory.html" rel="noreferrer">ForkJoinWorkerThreadFactory</a> with a system property will help in this case. Or would it?</p>
  
  <p><strong>Edit to answer the questions from the comments:</strong></p>
  
  <ul>
  <li><p><em>where do you deploy to? Is this running within jetty / tomcat / any JEE container?</em></p>
  
  <ul>
  <li>I'm using the default Spring Boot setup so an internal tomcat container is used.</li>
  </ul></li>
  <li><p><em>What is the exact issue you have?</em></p>
  
  <ul>
  <li>The exact issue is: <strong>java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader</strong></li>
  </ul></li>
  <li><p><em>The jobs that you submit to supplyAsync() are created from the AppClassLoader, aren't they?</em></p>
  
  <ul>
  <li><p>The <code>supplyAsync</code> is called from the <code>MainThread</code> which uses the <code>AppClassLoader</code>. But, debugging the applications shows that all such threads have <code>PlatformClassLoader</code> as their parent. As to my understanding, this happens because <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html#commonPool--" rel="noreferrer">ForkJoinPool.commonPool()</a> is constructed during the application startup (because it's static) and so uses the default class loader as the parent which is <code>PlatformClassLoader</code>. So, all threads from this pool get <code>PlatformClassLoader</code> as their parent for <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#getContextClassLoader--" rel="noreferrer">ContextClassLoader</a> (instead of <code>AppClassLoader</code>).</p></li>
  <li><p>When I'm creating my own executor inside the <code>MainThread</code> and pass this executor to <code>supplyAsync</code> everything works - and I can see during debugging that indeed now <code>AppClassLoader</code> is the parent of my <code>ThreadClassLoader</code>. Which seems to affirm my assumption in the first case that the common pool is not created by <code>MainThread</code> at least not when it's using <code>AppClassLoader</code> itself.</p></li>
  </ul></li>
  </ul>
  
  <p><strong>Full stacktrace:</strong></p>
  
  <pre><code>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.&lt;init&gt;(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]
  </code></pre>