Ensure that listeners are called when application fails to run

Closes gh-9054
pull/9258/merge
Andy Wilkinson 8 years ago
parent ad629055fa
commit aa6dbdbae2

@ -26,6 +26,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ErrorHandler; import org.springframework.util.ErrorHandler;
@ -94,12 +95,20 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
@Override @Override
public void finished(ConfigurableApplicationContext context, Throwable exception) { public void finished(ConfigurableApplicationContext context, Throwable exception) {
SpringApplicationEvent event = getFinishedEvent(context, exception); SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null) { if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should // Listeners have been registered to the application context so we should
// use it at this point if we can // use it at this point if we can
context.publishEvent(event); context.publishEvent(event);
} }
else { else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
if (event instanceof ApplicationFailedEvent) { if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
} }

@ -36,18 +36,21 @@ import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
@ -722,11 +725,107 @@ public class SpringApplicationTests {
private void verifyTestListenerEvents() { private void verifyTestListenerEvents() {
ApplicationListener<ApplicationEvent> listener = this.context ApplicationListener<ApplicationEvent> listener = this.context
.getBean("testApplicationListener", ApplicationListener.class); .getBean("testApplicationListener", ApplicationListener.class);
verify(listener).onApplicationEvent(argThat(isA(ContextRefreshedEvent.class))); verifyListenerEvents(listener, ContextRefreshedEvent.class,
verify(listener).onApplicationEvent(argThat(isA(ApplicationReadyEvent.class))); ApplicationReadyEvent.class);
}
@SuppressWarnings("unchecked")
private void verifyListenerEvents(ApplicationListener<ApplicationEvent> listener,
Class<? extends ApplicationEvent>... eventTypes) {
for (Class<? extends ApplicationEvent> eventType : eventTypes) {
verify(listener).onApplicationEvent(argThat(isA(eventType)));
}
verifyNoMoreInteractions(listener); verifyNoMoreInteractions(listener);
} }
@SuppressWarnings("unchecked")
@Test
public void applicationListenerFromApplicationIsCalledWhenContextFailsRefreshBeforeListenerRegistration() {
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.addListeners(listener);
try {
application.run();
fail("Run should have failed with an ApplicationContextException");
}
catch (ApplicationContextException ex) {
verifyListenerEvents(listener, ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class,
ApplicationPreparedEvent.class, ApplicationFailedEvent.class);
}
}
@SuppressWarnings("unchecked")
@Test
public void applicationListenerFromApplicationIsCalledWhenContextFailsRefreshAfterListenerRegistration() {
ApplicationListener<ApplicationEvent> listener = mock(ApplicationListener.class);
SpringApplication application = new SpringApplication(
BrokenPostConstructConfig.class);
application.setWebEnvironment(false);
application.addListeners(listener);
try {
application.run();
fail("Run should have failed with a BeanCreationException");
}
catch (BeanCreationException ex) {
verifyListenerEvents(listener, ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class,
ApplicationPreparedEvent.class, ApplicationFailedEvent.class);
}
}
@SuppressWarnings("unchecked")
@Test
public void applicationListenerFromContextIsCalledWhenContextFailsRefreshBeforeListenerRegistration() {
final ApplicationListener<ApplicationEvent> listener = mock(
ApplicationListener.class);
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.addInitializers(
new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(
ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(listener);
}
});
try {
application.run();
fail("Run should have failed with an ApplicationContextException");
}
catch (ApplicationContextException ex) {
verifyListenerEvents(listener, ApplicationFailedEvent.class);
}
}
@SuppressWarnings("unchecked")
@Test
public void applicationListenerFromContextIsCalledWhenContextFailsRefreshAfterListenerRegistration() {
final ApplicationListener<ApplicationEvent> listener = mock(
ApplicationListener.class);
SpringApplication application = new SpringApplication(
BrokenPostConstructConfig.class);
application.setWebEnvironment(false);
application.addInitializers(
new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(
ConfigurableApplicationContext applicationContext) {
applicationContext.addApplicationListener(listener);
}
});
try {
application.run();
fail("Run should have failed with a BeanCreationException");
}
catch (BeanCreationException ex) {
verifyListenerEvents(listener, ApplicationFailedEvent.class);
}
}
@Test @Test
public void registerShutdownHookOff() throws Exception { public void registerShutdownHookOff() throws Exception {
SpringApplication application = new SpringApplication(ExampleConfig.class); SpringApplication application = new SpringApplication(ExampleConfig.class);
@ -936,6 +1035,25 @@ public class SpringApplicationTests {
} }
@Configuration
static class BrokenPostConstructConfig {
@Bean
public Thing thing() {
return new Thing();
}
static class Thing {
@PostConstruct
public void boom() {
throw new IllegalStateException();
}
}
}
@Configuration @Configuration
static class ListenerConfig { static class ListenerConfig {

Loading…
Cancel
Save