Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refreshing from a background thread can skip autoconfiguration and miss config changes #840

Closed
hansenc opened this issue Oct 22, 2020 · 5 comments

Comments

@hansenc
Copy link

hansenc commented Oct 22, 2020

Describe the bug
Spring Boot 2.3.4.RELEASE w/ Spring Cloud Hoxton.SR8 (also tested 2.2.x w/ Hoxton.SR4) on Java 11

This may not be a bug, but it's behavior that was unexpected to me so I thought I'd report it here at least for posterity.

Normally, triggering a refresh (e.g. by calling RefreshEndpoint) causes autoconfiguration to run again, picking up config changes (among other things). However, when triggered from a background thread (e.g. via ApplicationEventPublisher.publishEvent(new RefreshEvent(this, event, desc))) it's possible that autoconfiguration classes, including PropertySourceBootstrapConfiguration, are not called so config changes are not picked up. Debugging through the code, it looks like the background thread's ContextClassLoader is used for org.springframework.core.io.support.SpringFactoriesLoader in the new (refreshed) bootstrap context and it might not find any spring.factories files, as is the case with java.util.concurrent.ForkJoinPool.commonPool(). Since ContextClassLoaders are inherited, I doubt this comes up often outside of the ForkJoin common pool.

Examples
When started via java -jar a org.springframework.boot.loader.LaunchedURLClassLoader is used by SpringFactoriesLoader on the initial bootstrap context and autoconfiguration works. When refreshing using RefreshEndpoint, a org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader is used by SpringFactoriesLoader and autoconfiguration works. When refreshing from a ForkJoin common pool thread, jdk.internal.loader.ClassLoaders.AppClassLoader (the JVM default I think) is used by SpringFactoriesLoader and no spring.factories files are found thus autoconfiguration is skipped.

Sample
I can provide a sample project if that will help, just let me know.

@spencergibb
Copy link
Member

I'm not sure there's anything we can do for auto-configuration. It is BeanClassLoaderAware. I'm unsure how to get it the right instance.

@hansenc
Copy link
Author

hansenc commented Nov 3, 2020

According to the JavaDoc you have to set a system property pointing to a custom ForkJoinPool.ForkJoinWorkerThreadFactory class which would be able to set the context class loader for the fork/join threads. Yuck. I suppose one or more thread factories could be included in a Spring Cloud jar so that anyone who needs this functionality could set the system property themselves. Edit: this might only work if the custom thread factory is included in the root jar of the application but I haven't tested this.

java.util.concurrent.ForkJoinPool.common.threadFactory - the class name of a ForkJoinPool.ForkJoinWorkerThreadFactory. The system class loader is used to load this class.

@hansenc
Copy link
Author

hansenc commented Nov 3, 2020

Related: spring-projects/spring-boot#15737

@spencergibb
Copy link
Member

Not sure there's anything to do.

@hansenc
Copy link
Author

hansenc commented Nov 3, 2020

I’ve been able to workaround this so I’m fine closing

@hansenc hansenc closed this as completed Nov 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants