Skip ContextCustomizerFactory that cannot be loaded
With this commit, if a ContextCustomizerFactory cannot be loaded due to a ClassNotFoundException or NoClassDefFoundError, we skip it and log a DEBUG message. This aligns with the "skipping" logic already in place for TestExecutionListeners. Closes gh-29034
This commit is contained in:
parent
ab20a189c5
commit
92b582701e
|
|
@ -200,8 +200,9 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
* @see SpringFactoriesLoader#load(Class, FailureHandler)
|
||||
*/
|
||||
protected List<TestExecutionListener> getDefaultTestExecutionListeners() {
|
||||
List<TestExecutionListener> listeners = SpringFactoriesLoader.forDefaultResourceLocation()
|
||||
.load(TestExecutionListener.class, this::handleListenerInstantiationFailure);
|
||||
SpringFactoriesLoader loader = SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader());
|
||||
List<TestExecutionListener> listeners =
|
||||
loader.load(TestExecutionListener.class, this::handleInstantiationFailure);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded default TestExecutionListener implementations from location [%s]: %s"
|
||||
.formatted(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames(listeners)));
|
||||
|
|
@ -219,7 +220,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
catch (BeanInstantiationException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof ClassNotFoundException || cause instanceof NoClassDefFoundError) {
|
||||
logSkippedListener(listenerClass.getName(), cause);
|
||||
logSkippedComponent(TestExecutionListener.class, listenerClass.getName(), cause);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
|
|
@ -229,46 +230,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
return listeners;
|
||||
}
|
||||
|
||||
private void handleListenerInstantiationFailure(
|
||||
Class<?> factoryType, String listenerClassName, Throwable failure) {
|
||||
|
||||
Throwable ex = (failure instanceof InvocationTargetException ite ?
|
||||
ite.getTargetException() : failure);
|
||||
|
||||
if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError) {
|
||||
logSkippedListener(listenerClassName, ex);
|
||||
}
|
||||
else if (ex instanceof LinkageError) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("""
|
||||
Could not load default TestExecutionListener [%s]. Specify custom \
|
||||
listener classes or make the default listener classes available."""
|
||||
.formatted(listenerClassName), ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ex instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
}
|
||||
if (ex instanceof Error error) {
|
||||
throw error;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Failed to load default TestExecutionListener [%s].".formatted(listenerClassName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void logSkippedListener(String listenerClassName, Throwable ex) {
|
||||
// TestExecutionListener not applicable due to a missing dependency
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("""
|
||||
Skipping candidate TestExecutionListener [%s] due to a missing dependency. \
|
||||
Specify custom listener classes or make the default listener classes \
|
||||
and their required dependencies available. Offending class: [%s]"""
|
||||
.formatted(listenerClassName, ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
@ -440,8 +401,9 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
* @see SpringFactoriesLoader#loadFactories
|
||||
*/
|
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
|
||||
SpringFactoriesLoader loader = SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader());
|
||||
List<ContextCustomizerFactory> factories =
|
||||
SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, getClass().getClassLoader());
|
||||
loader.load(ContextCustomizerFactory.class, this::handleInstantiationFailure);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded ContextCustomizerFactory implementations from location [%s]: %s"
|
||||
.formatted(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames(factories)));
|
||||
|
|
@ -570,6 +532,45 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
|||
}
|
||||
|
||||
|
||||
private void handleInstantiationFailure(
|
||||
Class<?> factoryType, String factoryImplementationName, Throwable failure) {
|
||||
|
||||
Throwable ex = (failure instanceof InvocationTargetException ite ?
|
||||
ite.getTargetException() : failure);
|
||||
if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError) {
|
||||
logSkippedComponent(factoryType, factoryImplementationName, ex);
|
||||
}
|
||||
else if (ex instanceof LinkageError) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("""
|
||||
Could not load %1$s [%2$s]. Specify custom %1$s classes or make the default %1$s classes \
|
||||
available.""".formatted(factoryType.getSimpleName(), factoryImplementationName), ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ex instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
}
|
||||
if (ex instanceof Error error) {
|
||||
throw error;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Failed to load %s [%s].".formatted(factoryType.getSimpleName(), factoryImplementationName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void logSkippedComponent(Class<?> factoryType, String factoryImplementationName, Throwable ex) {
|
||||
// TestExecutionListener/ContextCustomizerFactory not applicable due to a missing dependency
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("""
|
||||
Skipping candidate %1$s [%2$s] due to a missing dependency. \
|
||||
Specify custom %1$s classes or make the default %1$s classes \
|
||||
and their required dependencies available. Offending class: [%3$s]"""
|
||||
.formatted(factoryType.getSimpleName(), factoryImplementationName, ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static List<String> classNames(List<?> components) {
|
||||
return components.stream().map(Object::getClass).map(Class::getName).toList();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue