From d62c26c971257d9fe4a6920508fc08209086373f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 27 Jul 2017 08:44:32 +0100 Subject: [PATCH] Ensure that preinitialization has completed before run returns Previously, background preinitialization was started in response to an ApplicationEnvironmentPreparedEvent and would complete at an undetermined time later. This opened a window where SpringApplication run could return and background preinitialization could still be in progress. If, within this window, something attempted to configure the logging system, an IO failure could occur as logging on the background preinitialization thread would attempt to use resources that had been closed. This commit updates BackgroundPreinitializer so that it waits for preinitialization to have completed when it receives an application ready or application failed event. This prevents SpringApplication run from returning while preinitialization is still in progress, closing the window described above. With info level logging enabled it appears that background preinitialization consistently completes before the application ready event is published. As a result, waiting should have no adverse effect on performance in normal circumstances. With logging configured such that background preinitialization outputs a large volume of log messages (enabling trace logging for the root logger, for example), it will be slowed down sufficiently for waiting to be necessary. Closes gh-5669 --- .../BackgroundPreinitializer.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 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 73860b422c..cb7618d3ca 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,6 +16,7 @@ package org.springframework.boot.autoconfigure; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import javax.validation.Validation; @@ -23,6 +24,9 @@ import javax.validation.Validation; import org.apache.catalina.mbeans.MBeanFactory; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.SpringApplicationEvent; import org.springframework.boot.logging.LoggingApplicationListener; import org.springframework.context.ApplicationListener; import org.springframework.core.annotation.Order; @@ -40,14 +44,27 @@ import org.springframework.http.converter.support.AllEncompassingFormHttpMessage */ @Order(LoggingApplicationListener.DEFAULT_ORDER + 1) public class BackgroundPreinitializer - implements ApplicationListener { + implements ApplicationListener { private static final AtomicBoolean preinitalizationStarted = new AtomicBoolean(false); + private static final CountDownLatch preinitializationComplete = new CountDownLatch(1); + @Override - public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { - if (preinitalizationStarted.compareAndSet(false, true)) { - performPreinitialization(); + public void onApplicationEvent(SpringApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + if (preinitalizationStarted.compareAndSet(false, true)) { + performPreinitialization(); + } + } + if (event instanceof ApplicationReadyEvent + || event instanceof ApplicationFailedEvent) { + try { + preinitializationComplete.await(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } } } @@ -62,6 +79,7 @@ public class BackgroundPreinitializer runSafely(new ValidationInitializer()); runSafely(new JacksonInitializer()); runSafely(new ConversionServiceInitializer()); + preinitializationComplete.countDown(); } public void runSafely(Runnable runnable) { @@ -80,6 +98,7 @@ public class BackgroundPreinitializer // 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. + preinitializationComplete.countDown(); } }