Ensure that listeners are called when application fails to run
Closes gh-9054
This commit is contained in:
parent
ad629055fa
commit
aa6dbdbae2
|
|
@ -26,6 +26,7 @@ import org.springframework.context.ApplicationListener;
|
|||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
|
@ -94,12 +95,20 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
|
|||
@Override
|
||||
public void finished(ConfigurableApplicationContext context, Throwable 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
|
||||
// use it at this point if we can
|
||||
context.publishEvent(event);
|
||||
}
|
||||
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) {
|
||||
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,18 +36,21 @@ import org.junit.Test;
|
|||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
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.ApplicationReadyEvent;
|
||||
import org.springframework.boot.context.event.ApplicationStartingEvent;
|
||||
import org.springframework.boot.testutil.InternalOutputCapture;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
|
@ -722,11 +725,107 @@ public class SpringApplicationTests {
|
|||
private void verifyTestListenerEvents() {
|
||||
ApplicationListener<ApplicationEvent> listener = this.context
|
||||
.getBean("testApplicationListener", ApplicationListener.class);
|
||||
verify(listener).onApplicationEvent(argThat(isA(ContextRefreshedEvent.class)));
|
||||
verify(listener).onApplicationEvent(argThat(isA(ApplicationReadyEvent.class)));
|
||||
verifyListenerEvents(listener, ContextRefreshedEvent.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);
|
||||
}
|
||||
|
||||
@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
|
||||
public void registerShutdownHookOff() throws Exception {
|
||||
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
|
||||
static class ListenerConfig {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue