From 68983400fbd8daebdb674552cebdb095198f0a14 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 16 May 2016 17:26:03 -0700 Subject: [PATCH] Propogate startup failures to management context Update EndpointWebMvcAutoConfiguration so that ApplicationFailedEvents cause the management context to close. Prior to this commit if an application failed to start (for example because `server.port` was already in use) the management context would remain open and the application would not exit. Fixes gh-5388 --- .../EndpointWebMvcAutoConfiguration.java | 37 ++++++++++++++----- .../EndpointWebMvcAutoConfigurationTests.java | 18 +++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index 02c6c350359..63fd1d51309 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -54,9 +54,11 @@ import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; +import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.web.filter.ApplicationContextHeaderFilter; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -172,7 +174,7 @@ public class EndpointWebMvcAutoConfiguration EmbeddedServletContainerAutoConfiguration.class, DispatcherServletAutoConfiguration.class); registerEmbeddedServletContainerFactory(childContext); - CloseEventPropagationListener.addIfPossible(this.applicationContext, + CloseManagementContextListener.addIfPossible(this.applicationContext, childContext); childContext.refresh(); managementContextResolver().setApplicationContext(childContext); @@ -235,25 +237,42 @@ public class EndpointWebMvcAutoConfiguration } /** - * {@link ApplicationListener} to propagate the {@link ContextClosedEvent} from a - * parent to a child. + * {@link ApplicationListener} to propagate the {@link ContextClosedEvent} and + * {@link ApplicationFailedEvent} from a parent to a child. */ - private static class CloseEventPropagationListener - implements ApplicationListener { + private static class CloseManagementContextListener + implements ApplicationListener { private final ApplicationContext parentContext; private final ConfigurableApplicationContext childContext; - CloseEventPropagationListener(ApplicationContext parentContext, + CloseManagementContextListener(ApplicationContext parentContext, ConfigurableApplicationContext childContext) { this.parentContext = parentContext; this.childContext = childContext; } @Override - public void onApplicationEvent(ContextClosedEvent event) { - if (event.getApplicationContext() == this.parentContext) { + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ContextClosedEvent) { + onContextClosedEvent((ContextClosedEvent) event); + } + if (event instanceof ApplicationFailedEvent) { + onApplicationFailedEvent((ApplicationFailedEvent) event); + } + }; + + private void onContextClosedEvent(ContextClosedEvent event) { + propagateCloseIfNecessary(event.getApplicationContext()); + } + + private void onApplicationFailedEvent(ApplicationFailedEvent event) { + propagateCloseIfNecessary(event.getApplicationContext()); + } + + private void propagateCloseIfNecessary(ApplicationContext applicationContext) { + if (applicationContext == this.parentContext) { this.childContext.close(); } } @@ -268,7 +287,7 @@ public class EndpointWebMvcAutoConfiguration private static void add(ConfigurableApplicationContext parentContext, ConfigurableApplicationContext childContext) { parentContext.addApplicationListener( - new CloseEventPropagationListener(parentContext, childContext)); + new CloseManagementContextListener(parentContext, childContext)); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index e471e94fbdb..19e700848a5 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -62,10 +62,12 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; +import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.testutil.Matched; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -282,6 +284,22 @@ public class EndpointWebMvcAutoConfigurationTests { assertContent("/endpoint", managementPort, "endpointoutput"); } + @Test + public void onDifferentPortWithPrimaryFailure() throws Exception { + this.applicationContext.register(RootConfig.class, EndpointConfig.class, + DifferentPortConfig.class, BaseConfiguration.class, + EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class); + this.applicationContext.refresh(); + ApplicationContext managementContext = this.applicationContext + .getBean(ManagementContextResolver.class).getApplicationContext(); + ApplicationFailedEvent event = mock(ApplicationFailedEvent.class); + given(event.getApplicationContext()).willReturn(this.applicationContext); + this.applicationContext.publishEvent(event); + assertThat(((ConfigurableApplicationContext) managementContext).isActive()) + .isFalse(); + this.applicationContext.close(); + } + @Test public void disabled() throws Exception { this.applicationContext.register(RootConfig.class, EndpointConfig.class,