Skip to content

Commit 1038dbb

Browse files
committed
[Core] Log warning when resources could not be loaded
Fixes: #2212
1 parent f83c53d commit 1038dbb

File tree

8 files changed

+78
-36
lines changed

8 files changed

+78
-36
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +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)
20+
* [Core] Log warnings when classes or resource could not be loaded ([#2235](https://github.com/cucumber/cucumber-jvm/issues/2235) M.P. Korstanje)
2121

2222
## [6.9.1] (2020-12-14)
2323

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

+17-17
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import java.nio.file.Path;
99
import java.util.ArrayList;
1010
import java.util.Collection;
11+
import java.util.Collections;
1112
import java.util.List;
1213
import java.util.Optional;
1314
import java.util.function.Consumer;
1415
import java.util.function.Function;
1516
import java.util.function.Predicate;
1617
import java.util.function.Supplier;
1718

19+
import static io.cucumber.core.resource.ClasspathSupport.classPathScanningExplanation;
1820
import static io.cucumber.core.resource.ClasspathSupport.determineFullyQualifiedClassName;
1921
import static io.cucumber.core.resource.ClasspathSupport.getUrisForPackage;
2022
import static io.cucumber.core.resource.ClasspathSupport.requireValidPackageName;
@@ -69,12 +71,18 @@ private List<Class<?>> findClassesForUris(List<URI> baseUris, String packageName
6971
}
7072

7173
private List<Class<?>> findClassesForUri(URI baseUri, String packageName, Predicate<Class<?>> classFilter) {
72-
List<Class<?>> classes = new ArrayList<>();
73-
pathScanner.findResourcesForUri(
74-
baseUri,
75-
path -> isNotModuleInfo(path) && isNotPackageInfo(path) && isClassFile(path),
76-
processClassFiles(packageName, classFilter, classes::add));
77-
return classes;
74+
try {
75+
List<Class<?>> classes = new ArrayList<>();
76+
pathScanner.findResourcesForUri(
77+
baseUri,
78+
path -> isNotModuleInfo(path) && isNotPackageInfo(path) && isClassFile(path),
79+
processClassFiles(packageName, classFilter, classes::add));
80+
return classes;
81+
82+
} catch (Exception e) {
83+
log.warn(e, () -> "Failed to find classes in '" + baseUri + "'.\n" + classPathScanningExplanation());
84+
}
85+
return Collections.emptyList();
7886
}
7987

8088
private static boolean isNotModuleInfo(Path path) {
@@ -105,17 +113,9 @@ private Function<Path, Consumer<Path>> processClassFiles(
105113
private Optional<Class<?>> safelyLoadClass(String fqn) {
106114
try {
107115
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");
116+
} catch (Throwable t) {
117+
UnrecoverableExceptions.rethrowIfUnrecoverable(t);
118+
log.warn(t, () -> "Failed to class '" + fqn + "'.\n" + classPathScanningExplanation());
119119
}
120120
return Optional.empty();
121121
}

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 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/ResourceScanner.java

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

3+
import io.cucumber.core.logging.Logger;
4+
import io.cucumber.core.logging.LoggerFactory;
5+
36
import java.net.URI;
47
import java.nio.file.Path;
58
import java.util.ArrayList;
69
import java.util.Collection;
10+
import java.util.Collections;
711
import java.util.List;
812
import java.util.Optional;
913
import java.util.function.BiFunction;
@@ -28,6 +32,8 @@
2832

2933
public final class ResourceScanner<R> {
3034

35+
private static final Logger log = LoggerFactory.getLogger(ClasspathScanner.class);
36+
3137
private static final Predicate<String> NULL_FILTER = x -> true;
3238
private final PathScanner pathScanner = new PathScanner();
3339
private final Supplier<ClassLoader> classLoaderSupplier;
@@ -57,12 +63,17 @@ private List<R> findResourcesForUri(
5763
Predicate<String> packageFilter,
5864
BiFunction<Path, Path, Resource> createResource
5965
) {
60-
List<R> resources = new ArrayList<>();
61-
pathScanner.findResourcesForUri(
62-
baseUri,
63-
canLoad,
64-
processResource(basePackageName, packageFilter, createResource, resources::add));
65-
return resources;
66+
try {
67+
List<R> resources = new ArrayList<>();
68+
pathScanner.findResourcesForUri(
69+
baseUri,
70+
canLoad,
71+
processResource(basePackageName, packageFilter, createResource, resources::add));
72+
return resources;
73+
} catch (Exception e) {
74+
log.warn(e, () -> "Failed to find resources in '" + baseUri + "'.\n");
75+
}
76+
return Collections.emptyList();
6677
}
6778

6879
private Function<Path, Consumer<Path>> processResource(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.cucumber.core.exception;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static io.cucumber.core.exception.UnrecoverableExceptions.rethrowIfUnrecoverable;
6+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
7+
import static org.junit.jupiter.api.Assertions.assertThrows;
8+
9+
class UnrecoverableExceptionsTest {
10+
11+
@Test
12+
void shouldRethrowUnrecoverableErrors() {
13+
Throwable t = new OutOfMemoryError();
14+
assertThrows(OutOfMemoryError.class, () -> rethrowIfUnrecoverable(t));
15+
}
16+
17+
@Test
18+
void shouldPassRecoverableErrors() {
19+
Throwable t = new IllegalArgumentException();
20+
assertDoesNotThrow(() -> rethrowIfUnrecoverable(t));
21+
}
22+
23+
}

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

+4-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.lang.reflect.Modifier;
1010
import java.util.function.BiConsumer;
1111

12+
import static io.cucumber.core.resource.ClasspathSupport.classPathScanningExplanation;
1213
import static io.cucumber.java.InvalidMethodException.createInvalidMethodException;
1314

1415
final class MethodScanner {
@@ -35,17 +36,9 @@ static void scan(Class<?> aClass, BiConsumer<Method, Annotation> consumer) {
3536
private static Method[] safelyGetMethods(Class<?> aClass) {
3637
try {
3738
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");
39+
} catch (Throwable t) {
40+
UnrecoverableExceptions.rethrowIfUnrecoverable(t);
41+
log.warn(t, () -> "Failed to load methods of class '" + aClass.getName() + "'.\n" + classPathScanningExplanation());
4942
}
5043
return new Method[0];
5144
}

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

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

33
import org.junit.jupiter.api.BeforeEach;
44
import org.junit.jupiter.api.Test;
5-
import org.mockito.Mockito;
65

76
import java.lang.annotation.Annotation;
87
import java.lang.reflect.Method;

0 commit comments

Comments
 (0)