Commit 992e90f4 authored by Andy Wilkinson's avatar Andy Wilkinson

Ensure that background preinit has completed before refresh returns

This commit is a continuation of the changes made in b85b6082. It
addresses an additional problem when testing applications where two
contexts are refreshed in quick succession. In this scenario, it
was possible, theoretically at least, for the first context’s background preinitialization to still be in progress and creating loggers when the
second is refreshed and resets the logger context.

This commit updates BackgroundPreinitializer so that, upon receipt of
a ContextRefreshedEvent, it waits for preinitialization to have
completed. In the scenario described above, this ensures that
preinitialization has completed before the call to refresh() for the
first context returns, thereby preventing it from running in parallel
with the refresh of the second context.

Closes gh-4871
parent 179467bd
......@@ -16,16 +16,15 @@
package org.springframework.boot.autoconfigure;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.validation.Validation;
import org.apache.catalina.mbeans.MBeanFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.logging.LoggingApplicationListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
......@@ -38,50 +37,60 @@ import org.springframework.http.converter.support.AllEncompassingFormHttpMessage
* @since 1.3.0
*/
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer
implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
public class BackgroundPreinitializer implements ApplicationListener<ApplicationEvent> {
private volatile Thread initializationThread;
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
submit(executor, new MessageConverterInitializer());
submit(executor, new MBeanFactoryInitializer());
submit(executor, new ValidationInitializer());
executor.shutdown();
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartedEvent) {
performInitialization();
}
catch (Exception ex) {
// This will fail on GAE where creating threads is prohibited. We can safely
// continue but startup will be slightly slower as the initialization will now
// happen on the main thread.
else if (event instanceof ContextRefreshedEvent) {
awaitInitialization();
}
}
private void submit(ExecutorService executor, Runnable runnable) {
executor.submit(new FailSafeRunnable(runnable));
}
/**
* Wrapper to ignore any thrown exceptions.
*/
private static class FailSafeRunnable implements Runnable {
private final Runnable delegate;
FailSafeRunnable(Runnable delegate) {
this.delegate = delegate;
}
private void performInitialization() {
try {
this.initializationThread = new Thread(new Runnable() {
@Override
public void run() {
runSafely(new MessageConverterInitializer());
runSafely(new MBeanFactoryInitializer());
runSafely(new ValidationInitializer());
}
public void runSafely(Runnable runnable) {
try {
this.delegate.run();
runnable.run();
}
catch (Throwable ex) {
// Ignore
}
}
}, "background-preinit");
this.initializationThread.start();
}
catch (Exception ex) {
// This will fail on GAE where creating threads is prohibited. We can safely
// continue but startup will be slightly slower as the initialization will now
// happen on the main thread.
}
}
private void awaitInitialization() {
try {
this.initializationThread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
finally {
this.initializationThread = null;
}
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment