Skip to content

Commit 92b5827

Browse files
committed
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
1 parent ab20a18 commit 92b5827

File tree

1 file changed

+45
-44
lines changed

1 file changed

+45
-44
lines changed

spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,9 @@ public final List<TestExecutionListener> getTestExecutionListeners() {
200200
* @see SpringFactoriesLoader#load(Class, FailureHandler)
201201
*/
202202
protected List<TestExecutionListener> getDefaultTestExecutionListeners() {
203-
List<TestExecutionListener> listeners = SpringFactoriesLoader.forDefaultResourceLocation()
204-
.load(TestExecutionListener.class, this::handleListenerInstantiationFailure);
203+
SpringFactoriesLoader loader = SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader());
204+
List<TestExecutionListener> listeners =
205+
loader.load(TestExecutionListener.class, this::handleInstantiationFailure);
205206
if (logger.isDebugEnabled()) {
206207
logger.debug("Loaded default TestExecutionListener implementations from location [%s]: %s"
207208
.formatted(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames(listeners)));
@@ -219,7 +220,7 @@ private List<TestExecutionListener> instantiateListeners(Class<? extends TestExe
219220
catch (BeanInstantiationException ex) {
220221
Throwable cause = ex.getCause();
221222
if (cause instanceof ClassNotFoundException || cause instanceof NoClassDefFoundError) {
222-
logSkippedListener(listenerClass.getName(), cause);
223+
logSkippedComponent(TestExecutionListener.class, listenerClass.getName(), cause);
223224
}
224225
else {
225226
throw ex;
@@ -229,46 +230,6 @@ private List<TestExecutionListener> instantiateListeners(Class<? extends TestExe
229230
return listeners;
230231
}
231232

232-
private void handleListenerInstantiationFailure(
233-
Class<?> factoryType, String listenerClassName, Throwable failure) {
234-
235-
Throwable ex = (failure instanceof InvocationTargetException ite ?
236-
ite.getTargetException() : failure);
237-
238-
if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError) {
239-
logSkippedListener(listenerClassName, ex);
240-
}
241-
else if (ex instanceof LinkageError) {
242-
if (logger.isDebugEnabled()) {
243-
logger.debug("""
244-
Could not load default TestExecutionListener [%s]. Specify custom \
245-
listener classes or make the default listener classes available."""
246-
.formatted(listenerClassName), ex);
247-
}
248-
}
249-
else {
250-
if (ex instanceof RuntimeException runtimeException) {
251-
throw runtimeException;
252-
}
253-
if (ex instanceof Error error) {
254-
throw error;
255-
}
256-
throw new IllegalStateException(
257-
"Failed to load default TestExecutionListener [%s].".formatted(listenerClassName), ex);
258-
}
259-
}
260-
261-
private void logSkippedListener(String listenerClassName, Throwable ex) {
262-
// TestExecutionListener not applicable due to a missing dependency
263-
if (logger.isDebugEnabled()) {
264-
logger.debug("""
265-
Skipping candidate TestExecutionListener [%s] due to a missing dependency. \
266-
Specify custom listener classes or make the default listener classes \
267-
and their required dependencies available. Offending class: [%s]"""
268-
.formatted(listenerClassName, ex.getMessage()));
269-
}
270-
}
271-
272233
/**
273234
* {@inheritDoc}
274235
*/
@@ -440,8 +401,9 @@ private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
440401
* @see SpringFactoriesLoader#loadFactories
441402
*/
442403
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
404+
SpringFactoriesLoader loader = SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader());
443405
List<ContextCustomizerFactory> factories =
444-
SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, getClass().getClassLoader());
406+
loader.load(ContextCustomizerFactory.class, this::handleInstantiationFailure);
445407
if (logger.isDebugEnabled()) {
446408
logger.debug("Loaded ContextCustomizerFactory implementations from location [%s]: %s"
447409
.formatted(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames(factories)));
@@ -570,6 +532,45 @@ protected MergedContextConfiguration processMergedContextConfiguration(MergedCon
570532
}
571533

572534

535+
private void handleInstantiationFailure(
536+
Class<?> factoryType, String factoryImplementationName, Throwable failure) {
537+
538+
Throwable ex = (failure instanceof InvocationTargetException ite ?
539+
ite.getTargetException() : failure);
540+
if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError) {
541+
logSkippedComponent(factoryType, factoryImplementationName, ex);
542+
}
543+
else if (ex instanceof LinkageError) {
544+
if (logger.isDebugEnabled()) {
545+
logger.debug("""
546+
Could not load %1$s [%2$s]. Specify custom %1$s classes or make the default %1$s classes \
547+
available.""".formatted(factoryType.getSimpleName(), factoryImplementationName), ex);
548+
}
549+
}
550+
else {
551+
if (ex instanceof RuntimeException runtimeException) {
552+
throw runtimeException;
553+
}
554+
if (ex instanceof Error error) {
555+
throw error;
556+
}
557+
throw new IllegalStateException(
558+
"Failed to load %s [%s].".formatted(factoryType.getSimpleName(), factoryImplementationName), ex);
559+
}
560+
}
561+
562+
private void logSkippedComponent(Class<?> factoryType, String factoryImplementationName, Throwable ex) {
563+
// TestExecutionListener/ContextCustomizerFactory not applicable due to a missing dependency
564+
if (logger.isDebugEnabled()) {
565+
logger.debug("""
566+
Skipping candidate %1$s [%2$s] due to a missing dependency. \
567+
Specify custom %1$s classes or make the default %1$s classes \
568+
and their required dependencies available. Offending class: [%3$s]"""
569+
.formatted(factoryType.getSimpleName(), factoryImplementationName, ex.getMessage()));
570+
}
571+
}
572+
573+
573574
private static List<String> classNames(List<?> components) {
574575
return components.stream().map(Object::getClass).map(Class::getName).toList();
575576
}

0 commit comments

Comments
 (0)