From 992e90f43df6d332187ba12cb631be1b63153a2e Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 20 Jan 2016 21:56:35 +0000 Subject: [PATCH] Ensure that background preinit has completed before refresh returns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is a continuation of the changes made in b85b608. 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 --- .../BackgroundPreinitializer.java | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java index c44ac890b1..764c2fe957 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java @@ -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,17 +37,42 @@ import org.springframework.http.converter.support.AllEncompassingFormHttpMessage * @since 1.3.0 */ @Order(LoggingApplicationListener.DEFAULT_ORDER + 1) -public class BackgroundPreinitializer - implements ApplicationListener { +public class BackgroundPreinitializer implements ApplicationListener { + + private volatile Thread initializationThread; @Override - public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationStartedEvent) { + performInitialization(); + } + else if (event instanceof ContextRefreshedEvent) { + awaitInitialization(); + } + } + + private void performInitialization() { try { - ExecutorService executor = Executors.newSingleThreadExecutor(); - submit(executor, new MessageConverterInitializer()); - submit(executor, new MBeanFactoryInitializer()); - submit(executor, new ValidationInitializer()); - executor.shutdown(); + 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 { + 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 @@ -57,31 +81,16 @@ public class BackgroundPreinitializer } } - 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 awaitInitialization() { + try { + this.initializationThread.join(); } - - @Override - public void run() { - try { - this.delegate.run(); - } - catch (Throwable ex) { - // Ignore - } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + finally { + this.initializationThread = null; } - } /**