Commit f89b99bd authored by Phillip Webb's avatar Phillip Webb

Allow FailureAnalizers without ApplicationContext

Update `SpringApplication` so that `FailureAnalyzers` apply even if
the `ApplicationContext` was not created. If no `ApplicationContext`
is available, only `FailureAnalyzer` instances that do not implement
any `Aware` interfaces are considered.

Closes gh-23710
parent 84f96033
...@@ -312,7 +312,6 @@ public class SpringApplication { ...@@ -312,7 +312,6 @@ public class SpringApplication {
stopWatch.start(); stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext(); DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null; ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty(); configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args); SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass); listeners.starting(bootstrapContext, this.mainApplicationClass);
...@@ -323,8 +322,6 @@ public class SpringApplication { ...@@ -323,8 +322,6 @@ public class SpringApplication {
Banner printedBanner = printBanner(environment); Banner printedBanner = printBanner(environment);
context = createApplicationContext(); context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup); context.setApplicationStartup(this.applicationStartup);
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context); refreshContext(context);
afterRefresh(context, applicationArguments); afterRefresh(context, applicationArguments);
...@@ -336,7 +333,7 @@ public class SpringApplication { ...@@ -336,7 +333,7 @@ public class SpringApplication {
callRunners(context, applicationArguments); callRunners(context, applicationArguments);
} }
catch (Throwable ex) { catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners); handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
...@@ -344,7 +341,7 @@ public class SpringApplication { ...@@ -344,7 +341,7 @@ public class SpringApplication {
listeners.running(context); listeners.running(context);
} }
catch (Throwable ex) { catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null); handleRunFailure(context, ex, null);
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
return context; return context;
...@@ -812,7 +809,7 @@ public class SpringApplication { ...@@ -812,7 +809,7 @@ public class SpringApplication {
} }
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) { SpringApplicationRunListeners listeners) {
try { try {
try { try {
handleExitCode(context, exception); handleExitCode(context, exception);
...@@ -821,7 +818,7 @@ public class SpringApplication { ...@@ -821,7 +818,7 @@ public class SpringApplication {
} }
} }
finally { finally {
reportFailure(exceptionReporters, exception); reportFailure(getExceptionReporters(context), exception);
if (context != null) { if (context != null) {
context.close(); context.close();
} }
...@@ -833,6 +830,16 @@ public class SpringApplication { ...@@ -833,6 +830,16 @@ public class SpringApplication {
ReflectionUtils.rethrowRuntimeException(exception); ReflectionUtils.rethrowRuntimeException(exception);
} }
private Collection<SpringBootExceptionReporter> getExceptionReporters(ConfigurableApplicationContext context) {
try {
return getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
}
catch (Throwable ex) {
return Collections.emptyList();
}
}
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) { private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
try { try {
for (SpringBootExceptionReporter reporter : exceptionReporters) { for (SpringBootExceptionReporter reporter : exceptionReporters) {
......
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -31,7 +31,6 @@ import org.springframework.context.EnvironmentAware; ...@@ -31,7 +31,6 @@ import org.springframework.context.EnvironmentAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
...@@ -61,42 +60,50 @@ final class FailureAnalyzers implements SpringBootExceptionReporter { ...@@ -61,42 +60,50 @@ final class FailureAnalyzers implements SpringBootExceptionReporter {
} }
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) { FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
Assert.notNull(context, "Context must not be null"); this.classLoader = (classLoader != null) ? classLoader : getClassLoader(context);
this.classLoader = (classLoader != null) ? classLoader : context.getClassLoader(); this.analyzers = loadFailureAnalyzers(context, this.classLoader);
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
} }
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) { private ClassLoader getClassLoader(ConfigurableApplicationContext context) {
List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader); return (context != null) ? context.getClassLoader() : null;
}
private List<FailureAnalyzer> loadFailureAnalyzers(ConfigurableApplicationContext context,
ClassLoader classLoader) {
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);
List<FailureAnalyzer> analyzers = new ArrayList<>(); List<FailureAnalyzer> analyzers = new ArrayList<>();
for (String analyzerName : analyzerNames) { for (String className : classNames) {
try { try {
Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor(); FailureAnalyzer analyzer = createAnalyzer(context, className);
ReflectionUtils.makeAccessible(constructor); if (analyzer != null) {
analyzers.add((FailureAnalyzer) constructor.newInstance()); analyzers.add(analyzer);
}
} }
catch (Throwable ex) { catch (Throwable ex) {
logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex); logger.trace(LogMessage.format("Failed to load %s", className), ex);
} }
} }
AnnotationAwareOrderComparator.sort(analyzers); AnnotationAwareOrderComparator.sort(analyzers);
return analyzers; return analyzers;
} }
private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) { private FailureAnalyzer createAnalyzer(ConfigurableApplicationContext context, String className) throws Exception {
for (FailureAnalyzer analyzer : analyzers) { Constructor<?> constructor = ClassUtils.forName(className, this.classLoader).getDeclaredConstructor();
prepareAnalyzer(context, analyzer); ReflectionUtils.makeAccessible(constructor);
} FailureAnalyzer analyzer = (FailureAnalyzer) constructor.newInstance();
} if (analyzer instanceof BeanFactoryAware || analyzer instanceof EnvironmentAware) {
if (context == null) {
private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) { logger.trace(LogMessage.format("Skipping %s due to missing context", className));
if (analyzer instanceof BeanFactoryAware) { return null;
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory()); }
} if (analyzer instanceof BeanFactoryAware) {
if (analyzer instanceof EnvironmentAware) { ((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment()); }
if (analyzer instanceof EnvironmentAware) {
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
}
} }
return analyzer;
} }
@Override @Override
......
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -85,8 +85,19 @@ class FailureAnalyzersTests { ...@@ -85,8 +85,19 @@ class FailureAnalyzersTests {
verify(failureAnalyzer, times(1)).analyze(failure); verify(failureAnalyzer, times(1)).analyze(failure);
} }
@Test
void createWithNullContextSkipsAwareAnalyzers() {
RuntimeException failure = new RuntimeException();
analyzeAndReport("basic.factories", failure, null);
verify(failureAnalyzer, times(1)).analyze(failure);
}
private void analyzeAndReport(String factoriesName, Throwable failure) { private void analyzeAndReport(String factoriesName, Throwable failure) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
analyzeAndReport(factoriesName, failure, context);
}
private void analyzeAndReport(String factoriesName, Throwable failure, AnnotationConfigApplicationContext context) {
ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName); ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName);
new FailureAnalyzers(context, classLoader).reportException(failure); new FailureAnalyzers(context, classLoader).reportException(failure);
} }
......
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