diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index d9ad22f5a4..183b8f3f22 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -42,7 +42,6 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.boot.Banner.Mode; import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.boot.bind.RelaxedPropertyResolver; -import org.springframework.boot.diagnostics.FailureAnalyzers; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; @@ -328,7 +327,7 @@ public class SpringApplication { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; - FailureAnalyzers analyzers = null; + Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); @@ -341,7 +340,8 @@ public class SpringApplication { bindToSpringApplication(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); - analyzers = new FailureAnalyzers(context); + exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, + new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); @@ -355,7 +355,7 @@ public class SpringApplication { return context; } catch (Throwable ex) { - handleRunFailure(context, listeners, analyzers, ex); + handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } } @@ -423,11 +423,11 @@ public class SpringApplication { SpringApplicationRunListener.class, types, this, args)); } - private Collection getSpringFactoriesInstances(Class type) { + private Collection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class[] {}); } - private Collection getSpringFactoriesInstances(Class type, + private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates @@ -853,15 +853,15 @@ public class SpringApplication { } private void handleRunFailure(ConfigurableApplicationContext context, - SpringApplicationRunListeners listeners, FailureAnalyzers analyzers, - Throwable exception) { + SpringApplicationRunListeners listeners, + Collection exceptionReporters, Throwable exception) { try { try { handleExitCode(context, exception); listeners.finished(context, exception); } finally { - reportFailure(analyzers, exception); + reportFailure(exceptionReporters, exception); if (context != null) { context.close(); } @@ -873,11 +873,14 @@ public class SpringApplication { ReflectionUtils.rethrowRuntimeException(exception); } - private void reportFailure(FailureAnalyzers analyzers, Throwable failure) { + private void reportFailure(Collection exceptionReporters, + Throwable failure) { try { - if (analyzers != null && analyzers.analyzeAndReport(failure)) { - registerLoggedException(failure); - return; + for (SpringBootExceptionReporter reporter : exceptionReporters) { + if (reporter.reportException(failure)) { + registerLoggedException(failure); + return; + } } } catch (Throwable ex) { diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringBootExceptionReporter.java b/spring-boot/src/main/java/org/springframework/boot/SpringBootExceptionReporter.java new file mode 100644 index 0000000000..3f2528bbe9 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/SpringBootExceptionReporter.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot; + +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.support.SpringFactoriesLoader; + +/** + * Callback interface used to support custom reporting of {@link SpringApplication} + * startup errors. {@link SpringBootExceptionReporter reporters} are loaded via the + * {@link SpringFactoriesLoader} and must declare a public constructor with a single + * {@link ConfigurableApplicationContext} parameter. + * + * @author Phillip Webb + * @since 2.0.0 + * @see ApplicationContextAware + */ +public interface SpringBootExceptionReporter { + + /** + * Report a startup failure to the user. + * @param failure the source failure + * @return {@code true} if the failure was reported or {@code false} if default + * reporting should occur. + */ + boolean reportException(Throwable failure); + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java b/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java index 477857170b..f558fc2288 100644 --- a/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java +++ b/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.boot.SpringBootExceptionReporter; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.io.support.SpringFactoriesLoader; @@ -44,9 +45,8 @@ import org.springframework.util.ReflectionUtils; * @author Andy Wilkinson * @author Phillip Webb * @author Stephane Nicoll - * @since 1.4.0 */ -public final class FailureAnalyzers { +final class FailureAnalyzers implements SpringBootExceptionReporter { private static final Log logger = LogFactory.getLog(FailureAnalyzers.class); @@ -54,12 +54,7 @@ public final class FailureAnalyzers { private final List analyzers; - /** - * Create a new {@link FailureAnalyzers} instance. - * @param context the source application context - * @since 1.4.1 - */ - public FailureAnalyzers(ConfigurableApplicationContext context) { + FailureAnalyzers(ConfigurableApplicationContext context) { this(context, null); } @@ -103,12 +98,8 @@ public final class FailureAnalyzers { } } - /** - * Analyze and report the specified {@code failure}. - * @param failure the failure to analyze - * @return {@code true} if the failure was handled - */ - public boolean analyzeAndReport(Throwable failure) { + @Override + public boolean reportException(Throwable failure) { FailureAnalysis analysis = analyze(failure, this.analyzers); return report(analysis, this.classLoader); } diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories index f5dd50ab5e..2d81ab15c4 100644 --- a/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -7,6 +7,10 @@ org.springframework.boot.env.YamlPropertySourceLoader org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener +# Error Reporters +org.springframework.boot.SpringBootExceptionReporter=\ +org.springframework.boot.diagnostics.FailureAnalyzers + # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ diff --git a/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java b/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java index 7329030222..0aebdeed17 100644 --- a/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java @@ -78,7 +78,7 @@ public class FailureAnalyzersTests { private void analyzeAndReport(String factoriesName, Throwable failure) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName); - new FailureAnalyzers(context, classLoader).analyzeAndReport(failure); + new FailureAnalyzers(context, classLoader).reportException(failure); } static class BasicFailureAnalyzer implements FailureAnalyzer {