3

First of all sorry for the 'problem' in the title, it's not to blame but I didn't find a suitable description.

Secondly, I'm aware of the issues and articles regarding this topic which I'll come to some lines later.

I'm aware of the 'problems' that arise out of the change in the classloader handling with ForkJoin common pools introduced in (I think) Java 9 to fix the bug JDK-8172726. There are several issues at GitHub/sprin-boot regarding this which are all closed because of 'not a Spring issue but a result of the mentioned JDK fix':

I'm also aware that this only occurs when running a Spring Boot application with java -jar build/libs/my-application-0.0.1-SNAPSHOT.jar and not via gradle bootRun or within my IDE.

When running with java -jar ... the Spring Boot classloader packaged withing the created jar file comes into play and the order of class files and jar files in the class path is changed.

I'm also aware that when running a Spring Boot application from the exploded jar file via java -cp "BOOT-INF/classes:BOOT-INF/lib/*" com.example.MyApplication the problem does not arise because instead of the Spring Boot classloader the AppClassLoader is used.

The problem described in short:
When using a ForkJoin pool (Stream.parallel().forEach(...)) the thread calling this uses the Spring Boot classloader while the worker threads are using the AppClassLoader which 'sometimes' (it's quite not clear to me under which conditions) fails to load classes that the Spring Boot classloader is aware of.

I've written a very small Spring Boot example application that nicely shows that: https://github.com/fnumrich/cnf/tree/main

What my example application does (log statements removed):

IntStream.range(0, 500).parallel().forEach(i -> {
 try {
 // Fails in worker threads with 
 // java.lang.ClassNotFoundException: org.glassfish.jaxb.runtime.v2.ContextFactory
 JAXBContext.newInstance(ObjectFactory.class);
 } catch (Exception e) {
 try {
 // Succeeds in worker threads
 Class.forName("org.glassfish.jaxb.runtime.v2.ContextFactory");
 } catch (ClassNotFoundException ex) {
 ...
 }
 }
});

If a (worker-)thread fails to create a new JAXBContext because of java.lang.ClassNotFoundException: org.glassfish.jaxb.runtime.v2.ContextFactory the application tries to load exactly this class via Class.forName() which interestingly succeeeds ...

So there are several questions that are not clear to me:

  1. What is the condition for the worker threads to run into this situation?
    There are 'only some' situations where the worker threads with the AppClassLoader are running into a ClassNotFoundException. The one described here (JAXBContext.newInstance(ObjectFactory.class)), another that I've come over and asked here: Java ServiceLoader sometimes does not find registered services
  2. When a worker thread fails to execute JAXBContext.newInstance(ObjectFactory.class) with ClassNotFoundException: org.glassfish.jaxb.runtime.v2.ContextFactory - why is this thread able to successfully execute Class.forName("org.glassfish.jaxb.runtime.v2.ContextFactory")
  3. And the main question: It's quite not clear to me how this should be handled correctly. There are several suggestions out there but all of them result in running a Spring Boot application via java -jar ... (using the Spring Boot classloader) and using Streams.parallel() or more general using the 'standard' ForkJoinPool without further preparation is incompatible ...

So at least I am a little confused. Maybe some of the spring dudes is able to bring some light in this situation.

asked Feb 17, 2024 at 18:54
2
  • 1
    No comments from the Spring Boot team? Commented Feb 22, 2024 at 9:23
  • 1
    I'm having a very similar problem but using a thread pool etc. doesn't seem to play a big role. We are using Spring Boot 3 (added manually; not using spring parent pom). org.jboss.resteasy.client.jaxrs.internal.proxy.ProxyBuilderImpl fails to load using the Spring Boot classloader. When jars are exploded or normal it all works like a charm. In any thread etc. if using Spring Boot classloader it always fails. It doesn't matter if its the first call in main or after spring fully initialized. This is breaking my mind ... just wtf. Commented Apr 23, 2024 at 13:39

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.