Skip to content

Commit f83c53d

Browse files
committed
[Core] Log warning when classes could not be loaded
Fixes: #2229
1 parent c273364 commit f83c53d

File tree

5 files changed

+63
-8
lines changed

5 files changed

+63
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717

1818
### Fixed
1919
* [Core] Pass class loader to ServiceLoader.load invocations ([#2220](https://github.com/cucumber/cucumber-jvm/issues/2220) M.P. Korstanje)
20+
* [Core] Log warnings when classes could not be loaded ([#2235](https://github.com/cucumber/cucumber-jvm/issues/2235) M.P. Korstanje)
2021

2122
## [6.9.1] (2020-12-14)
2223

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.cucumber.core.exception;
2+
3+
public final class UnrecoverableExceptions {
4+
5+
private UnrecoverableExceptions() {
6+
}
7+
8+
public static void rethrowIfUnrecoverable(Throwable throwable) {
9+
if (throwable instanceof OutOfMemoryError) {
10+
ExceptionUtils.throwAsUncheckedException(throwable);
11+
}
12+
}
13+
14+
}

core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.cucumber.core.resource;
22

3+
import io.cucumber.core.exception.UnrecoverableExceptions;
34
import io.cucumber.core.logging.Logger;
45
import io.cucumber.core.logging.LoggerFactory;
56

@@ -95,16 +96,30 @@ private Function<Path, Consumer<Path>> processClassFiles(
9596
) {
9697
return baseDir -> classFile -> {
9798
String fqn = determineFullyQualifiedClassName(baseDir, basePackageName, classFile);
98-
try {
99-
Optional.of(getClassLoader().loadClass(fqn))
100-
.filter(classFilter)
101-
.ifPresent(classConsumer);
102-
} catch (ClassNotFoundException | NoClassDefFoundError e) {
103-
log.debug(e, () -> "Failed to load class " + fqn);
104-
}
99+
safelyLoadClass(fqn)
100+
.filter(classFilter)
101+
.ifPresent(classConsumer);
105102
};
106103
}
107104

105+
private Optional<Class<?>> safelyLoadClass(String fqn) {
106+
try {
107+
return Optional.ofNullable(getClassLoader().loadClass(fqn));
108+
} catch (Throwable e) {
109+
UnrecoverableExceptions.rethrowIfUnrecoverable(e);
110+
log.warn(e, () -> "" +
111+
"Failed to class '" + fqn + "'.\n" +
112+
"By default Cucumber scans the classpath for step definitions.\n" +
113+
"You can restrict this by configuring the glue path.\n" +
114+
"\n" +
115+
"Examples:\n" +
116+
" - @CucumberOptions(glue = \"com.example.application\")\n" +
117+
" - src/test/resources/junit-platform.properties cucumber.glue=com.example.application\n" +
118+
" - src/test/resources/cucumber.properties cucumber.glue=com.example.application\n");
119+
}
120+
return Optional.empty();
121+
}
122+
108123
public List<Class<?>> scanForClassesInPackage(String packageName) {
109124
return scanForClassesInPackage(packageName, NULL_FILTER);
110125
}

java/src/main/java/io/cucumber/java/MethodScanner.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package io.cucumber.java;
22

3+
import io.cucumber.core.exception.UnrecoverableExceptions;
4+
import io.cucumber.core.logging.Logger;
5+
import io.cucumber.core.logging.LoggerFactory;
6+
37
import java.lang.annotation.Annotation;
48
import java.lang.reflect.Method;
59
import java.lang.reflect.Modifier;
@@ -9,6 +13,8 @@
913

1014
final class MethodScanner {
1115

16+
private static final Logger log = LoggerFactory.getLogger(MethodScanner.class);
17+
1218
private MethodScanner() {
1319
}
1420

@@ -21,11 +27,29 @@ static void scan(Class<?> aClass, BiConsumer<Method, Annotation> consumer) {
2127
if (!isInstantiable(aClass)) {
2228
return;
2329
}
24-
for (Method method : aClass.getMethods()) {
30+
for (Method method : safelyGetMethods(aClass)) {
2531
scan(consumer, aClass, method);
2632
}
2733
}
2834

35+
private static Method[] safelyGetMethods(Class<?> aClass) {
36+
try {
37+
return aClass.getMethods();
38+
} catch (Throwable e) {
39+
UnrecoverableExceptions.rethrowIfUnrecoverable(e);
40+
log.warn(e, () -> "" +
41+
"Failed to load methods of class '" + aClass.getName() + "'.\n" +
42+
"By default Cucumber scans the classpath for step definitions.\n" +
43+
"You can restrict this by configuring the glue path.\n" +
44+
"\n" +
45+
"Examples:\n" +
46+
" - @CucumberOptions(glue = \"com.example.application\")\n" +
47+
" - src/test/resources/junit-platform.properties cucumber.glue=com.example.application\n" +
48+
" - src/test/resources/cucumber.properties cucumber.glue=com.example.application\n");
49+
}
50+
return new Method[0];
51+
}
52+
2953
private static boolean isInstantiable(Class<?> clazz) {
3054
boolean isNonStaticInnerClass = !Modifier.isStatic(clazz.getModifiers()) && clazz.getEnclosingClass() != null;
3155
return Modifier.isPublic(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers())

java/src/test/java/io/cucumber/java/MethodScannerTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.junit.jupiter.api.BeforeEach;
44
import org.junit.jupiter.api.Test;
5+
import org.mockito.Mockito;
56

67
import java.lang.annotation.Annotation;
78
import java.lang.reflect.Method;

0 commit comments

Comments
 (0)