Commit 285dd5b2 authored by Dave Syer's avatar Dave Syer

ApplicationContextInitializers now listen for ContextRefreshedEvent

The AutoConfigurationReportLoggingInitializer wasn't working in
non-GenericApplicationContext becasue teh BeanFatcory wasn't available
for registering its listener during initialization. Instead of
relying on that rather fragile state I decided to give any
ApplicationContextInitializer that was itself an ApplicationListener
an explicit callback with a ContextRefreshedEvent, and move that
interface up a level in the logging initializer. Works much better.
parent f3a225f3
......@@ -20,7 +20,6 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationErrorHandler;
import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcome;
......@@ -30,6 +29,7 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
......@@ -49,18 +49,21 @@ import org.springframework.util.StringUtils;
*/
public class AutoConfigurationReportLoggingInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>,
SpringApplicationErrorHandler {
SpringApplicationErrorHandler, ApplicationListener<ContextRefreshedEvent> {
private static final String LOGGER_BEAN = "autoConfigurationReportLogger";
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableApplicationContext applicationContext;
private AutoConfigurationReportLogger loggerBean;
private AutoConfigurationReport report;
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
this.loggerBean = new AutoConfigurationReportLogger(applicationContext);
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
if (!beanFactory.containsSingleton(LOGGER_BEAN)) {
beanFactory.registerSingleton(LOGGER_BEAN, this.loggerBean);
this.applicationContext = applicationContext;
if (applicationContext instanceof GenericApplicationContext) {
// Get the report early in case the context fails to load
this.report = AutoConfigurationReport.get(this.applicationContext
.getBeanFactory());
}
}
......@@ -68,30 +71,7 @@ public class AutoConfigurationReportLoggingInitializer implements
public void handleError(SpringApplication application,
ConfigurableApplicationContext applicationContext, String[] args,
Throwable exception) {
if (this.loggerBean != null) {
this.loggerBean.logAutoConfigurationReport(true);
}
}
/**
* Spring bean to actually perform the logging.
*/
public static class AutoConfigurationReportLogger implements
ApplicationListener<ContextRefreshedEvent> {
private final Log logger = LogFactory.getLog(getClass());
private final ConfigurableApplicationContext applicationContext;
private final AutoConfigurationReport report;
public AutoConfigurationReportLogger(
ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
// Get the report early in case the context fails to load
this.report = AutoConfigurationReport.get(this.applicationContext
.getBeanFactory());
logAutoConfigurationReport(true);
}
@Override
......@@ -106,6 +86,10 @@ public class AutoConfigurationReportLoggingInitializer implements
}
void logAutoConfigurationReport(boolean isCrashReport) {
if (this.report == null) {
this.report = AutoConfigurationReport.get(this.applicationContext
.getBeanFactory());
}
if (this.report.getConditionAndOutcomesBySource().size() > 0) {
if (isCrashReport && this.logger.isInfoEnabled()) {
this.logger.info(getLogMessage(this.report
......@@ -160,7 +144,7 @@ public class AutoConfigurationReportLoggingInitializer implements
.getClass()));
message.append(")\n");
}
}
}
}
......@@ -29,16 +29,19 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.boot.autoconfigure.AutoConfigurationReportLoggingInitializer.AutoConfigurationReportLogger;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
......@@ -104,6 +107,7 @@ public class AutoConfigurationReportLoggingInitializerTests {
this.initializer.initialize(context);
context.register(Config.class);
context.refresh();
this.initializer.onApplicationEvent(new ContextRefreshedEvent(context));
assertThat(this.debugLog.size(), not(equalTo(0)));
}
......@@ -130,6 +134,7 @@ public class AutoConfigurationReportLoggingInitializerTests {
this.initializer.initialize(context);
context.register(Config.class);
context.refresh();
this.initializer.onApplicationEvent(new ContextRefreshedEvent(context));
for (String message : this.debugLog) {
System.out.println(message);
}
......@@ -138,10 +143,29 @@ public class AutoConfigurationReportLoggingInitializerTests {
assertThat(l, containsString("not a web application (OnWebApplicationCondition)"));
}
@Test
public void canBeUsedInApplicationContext() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class);
new AutoConfigurationReportLoggingInitializer().initialize(context);
context.refresh();
assertNotNull(context.getBean(AutoConfigurationReport.class));
}
@Test
public void canBeUsedInNonGenericApplicationContext() throws Exception {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(Config.class);
new AutoConfigurationReportLoggingInitializer().initialize(context);
context.refresh();
assertNotNull(context.getBean(AutoConfigurationReport.class));
}
public static class MockLogFactory extends LogFactoryImpl {
@Override
public Log getInstance(String name) throws LogConfigurationException {
if (AutoConfigurationReportLogger.class.getName().equals(name)) {
if (AutoConfigurationReportLoggingInitializer.class.getName().equals(name)) {
return logThreadLocal.get();
}
return new NoOpLog();
......
......@@ -36,12 +36,16 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.GenericTypeResolver;
......@@ -298,7 +302,7 @@ public class SpringApplication {
getApplicationLog(), stopWatch);
}
runCommandLineRunners(context, args);
afterRefresh(context, args);
return context;
}
catch (RuntimeException ex) {
......@@ -312,6 +316,19 @@ public class SpringApplication {
}
private void afterRefresh(ConfigurableApplicationContext context, String[] args) {
ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
getInitializers());
for (ApplicationContextInitializer<?> initializer : initializers) {
if (initializer instanceof ApplicationListener) {
multicaster.addApplicationListener((ApplicationListener<?>) initializer);
}
}
multicaster.multicastEvent(new ContextRefreshedEvent(context));
runCommandLineRunners(context, args);
}
private void handleError(ConfigurableApplicationContext context, String[] args,
Throwable exception) {
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
......
......@@ -33,11 +33,13 @@ import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletConta
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.CommandLinePropertySource;
......@@ -156,6 +158,30 @@ public class SpringApplicationTests {
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
}
@Test
public void contextRefreshedEventListener() throws Exception {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebEnvironment(false);
final AtomicReference<ApplicationContext> reference = new AtomicReference<ApplicationContext>();
class InitalizerListener implements
ApplicationContextInitializer<ConfigurableApplicationContext>,
ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
reference.set(event.getApplicationContext());
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
}
}
application.setInitializers(Arrays.asList(new InitalizerListener()));
this.context = application.run("--foo=bar");
assertThat(this.context, sameInstance(reference.get()));
// Custom initializers do not switch off the defaults
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
}
@Test
public void defaultApplicationContext() throws Exception {
SpringApplication application = new SpringApplication(ExampleConfig.class);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment