Skip to content

Commit b6ebbaf

Browse files
committed
[Core] Log warning when resources could not be loaded
Fixes: #2212, #2229
1 parent c273364 commit b6ebbaf

File tree

7 files changed

+53
-9
lines changed

7 files changed

+53
-9
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 or resource 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

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

+13-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.function.Predicate;
1515
import java.util.function.Supplier;
1616

17+
import static io.cucumber.core.resource.ClasspathSupport.classPathScanningExplanation;
1718
import static io.cucumber.core.resource.ClasspathSupport.determineFullyQualifiedClassName;
1819
import static io.cucumber.core.resource.ClasspathSupport.getUrisForPackage;
1920
import static io.cucumber.core.resource.ClasspathSupport.requireValidPackageName;
@@ -95,16 +96,21 @@ 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 (ClassNotFoundException | NoClassDefFoundError e) {
109+
log.warn(e, () -> "Failed to load class '" + fqn + "'.\n" + classPathScanningExplanation());
110+
}
111+
return Optional.empty();
112+
}
113+
108114
public List<Class<?>> scanForClassesInPackage(String packageName) {
109115
return scanForClassesInPackage(packageName, NULL_FILTER);
110116
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,14 @@ public static URI rootPackageUri() {
153153
return URI.create(CLASSPATH_SCHEME_PREFIX + RESOURCE_SEPARATOR_CHAR);
154154
}
155155

156+
public static String classPathScanningExplanation() {
157+
return "By default Cucumber scans the entire classpath for step definitions.\n" +
158+
"You can restrict this by configuring the glue path.\n" +
159+
"\n" +
160+
"Examples:\n" +
161+
" - @CucumberOptions(glue = \"com.example.application\")\n" +
162+
" - src/test/resources/junit-platform.properties cucumber.glue=com.example.application\n" +
163+
" - src/test/resources/cucumber.properties cucumber.glue=com.example.application\n";
164+
}
165+
156166
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private static CloseablePath handleJarUriScheme(URI uri) throws IOException, URI
101101

102102
private static CucumberException nestedJarEntriesAreUnsupported(URI uri) {
103103
return new CucumberException("" +
104-
"The resource " + uri + " is located in a nested jar.\n" +
104+
"The resource '" + uri + "' is located in a nested jar.\n" +
105105
"\n" +
106106
"This typically happens when trying to run Cucumber inside a Spring Boot Executable Jar.\n" +
107107
"Cucumber currently doesn't support classpath scanning in nested jars.\n" +

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

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.io.IOException;
77
import java.net.URI;
88
import java.net.URISyntaxException;
9+
import java.nio.file.FileSystemNotFoundException;
910
import java.nio.file.FileVisitOption;
1011
import java.nio.file.FileVisitResult;
1112
import java.nio.file.Path;
@@ -22,10 +23,14 @@
2223

2324
class PathScanner {
2425

26+
private static final Logger log = LoggerFactory.getLogger(PathScanner.class);
27+
2528
void findResourcesForUri(URI baseUri, Predicate<Path> filter, Function<Path, Consumer<Path>> consumer) {
2629
try (CloseablePath closeablePath = open(baseUri)) {
2730
Path baseDir = closeablePath.getPath();
2831
findResourcesForPath(baseDir, filter, consumer);
32+
} catch (FileSystemNotFoundException e) {
33+
log.warn(e, () -> "Failed to find resources for '" + baseUri + "'");
2934
} catch (IOException | URISyntaxException e) {
3035
throw new RuntimeException(e);
3136
}

core/src/test/java/io/cucumber/core/resource/ResourceScannerTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ void scanForResourcesDirectory() {
124124
new File("src/test/resources/io/cucumber/core/resource/test/spaces in name resource.txt").toURI()));
125125
}
126126

127+
@Test
128+
void shouldThrowIfForResourcesPathNotExist() {
129+
File file = new File("src/test/resources/io/cucumber/core/does/not/exist");
130+
assertThrows(IllegalArgumentException.class, () -> resourceScanner.scanForResourcesPath(file.toPath()));
131+
}
132+
127133
@Test
128134
@DisabledOnOs(value = OS.WINDOWS,
129135
disabledReason = "Only works if repository is explicitly cloned activated symlinks and " +

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

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package io.cucumber.java;
22

3+
import io.cucumber.core.logging.Logger;
4+
import io.cucumber.core.logging.LoggerFactory;
5+
36
import java.lang.annotation.Annotation;
47
import java.lang.reflect.Method;
58
import java.lang.reflect.Modifier;
69
import java.util.function.BiConsumer;
710

11+
import static io.cucumber.core.resource.ClasspathSupport.classPathScanningExplanation;
812
import static io.cucumber.java.InvalidMethodException.createInvalidMethodException;
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,21 @@ 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 (NoClassDefFoundError e) {
39+
log.warn(e,
40+
() -> "Failed to load methods of class '" + aClass.getName() + "'.\n" + classPathScanningExplanation());
41+
}
42+
return new Method[0];
43+
}
44+
2945
private static boolean isInstantiable(Class<?> clazz) {
3046
boolean isNonStaticInnerClass = !Modifier.isStatic(clazz.getModifiers()) && clazz.getEnclosingClass() != null;
3147
return Modifier.isPublic(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers())

0 commit comments

Comments
 (0)