From 4b261e6c71a293fe38f9964a460350690ec73af3 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sat, 23 Jan 2021 18:45:59 +0100 Subject: [PATCH] [Core] Pass class loader to ServiceLoader.load invocations Fixes: #2217, #2219 --- CHANGELOG.md | 1 + .../core/runtime/BackendServiceLoader.java | 3 ++- .../runtime/ObjectFactoryServiceLoader.java | 17 +++++++++++++---- .../java/io/cucumber/core/runtime/Runtime.java | 2 +- .../core/runtime/BackendServiceLoaderTest.java | 18 ++++++++++-------- .../runtime/SingletonRunnerSupplierTest.java | 3 ++- .../runtime/ThreadLocalRunnerSupplierTest.java | 3 ++- .../engine/CucumberEngineExecutionContext.java | 2 +- .../main/java/io/cucumber/junit/Cucumber.java | 3 ++- .../io/cucumber/junit/FeatureRunnerTest.java | 2 +- .../cucumber/testng/TestNGCucumberRunner.java | 3 ++- 11 files changed, 37 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1451a0c00..8a86a61437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### Fixed + * [Core] Pass class loader to ServiceLoader.load invocations ([#2220](https://github.com/cucumber/cucumber-jvm/issues/2220) M.P. Korstanje) ## [6.9.1] (2020-12-14) diff --git a/core/src/main/java/io/cucumber/core/runtime/BackendServiceLoader.java b/core/src/main/java/io/cucumber/core/runtime/BackendServiceLoader.java index 3f82ca04a9..da15bd8167 100644 --- a/core/src/main/java/io/cucumber/core/runtime/BackendServiceLoader.java +++ b/core/src/main/java/io/cucumber/core/runtime/BackendServiceLoader.java @@ -29,7 +29,8 @@ public BackendServiceLoader( @Override public Collection get() { - return get(ServiceLoader.load(BackendProviderService.class)); + ClassLoader classLoader = classLoaderSupplier.get(); + return get(ServiceLoader.load(BackendProviderService.class, classLoader)); } Collection get(Iterable serviceLoader) { diff --git a/core/src/main/java/io/cucumber/core/runtime/ObjectFactoryServiceLoader.java b/core/src/main/java/io/cucumber/core/runtime/ObjectFactoryServiceLoader.java index da5be9c616..f8258d8777 100644 --- a/core/src/main/java/io/cucumber/core/runtime/ObjectFactoryServiceLoader.java +++ b/core/src/main/java/io/cucumber/core/runtime/ObjectFactoryServiceLoader.java @@ -9,16 +9,25 @@ import java.util.Iterator; import java.util.Map; import java.util.ServiceLoader; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.lang.Thread.currentThread; import static java.util.Objects.requireNonNull; public final class ObjectFactoryServiceLoader { + private final Supplier classLoaderSupplier; private final Options options; + @Deprecated public ObjectFactoryServiceLoader(Options options) { + this(currentThread()::getContextClassLoader, options); + } + + public ObjectFactoryServiceLoader(Supplier classLoaderSupplier, Options options) { + this.classLoaderSupplier = requireNonNull(classLoaderSupplier); this.options = requireNonNull(options); } @@ -38,9 +47,9 @@ public ObjectFactoryServiceLoader(Options options) { * @return an instance of {@link ObjectFactory} */ ObjectFactory loadObjectFactory() { - Class objectFactoryClass = this.options.getObjectFactoryClass(); - - final ServiceLoader loader = ServiceLoader.load(ObjectFactory.class); + Class objectFactoryClass = options.getObjectFactoryClass(); + ClassLoader classLoader = classLoaderSupplier.get(); + ServiceLoader loader = ServiceLoader.load(ObjectFactory.class, classLoader); if (objectFactoryClass == null) { return loadSingleObjectFactoryOrDefault(loader); @@ -50,7 +59,7 @@ ObjectFactory loadObjectFactory() { } private static ObjectFactory loadSingleObjectFactoryOrDefault(ServiceLoader loader) { - final Iterator objectFactories = loader.iterator(); + Iterator objectFactories = loader.iterator(); ObjectFactory objectFactory; if (objectFactories.hasNext()) { diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index b8a96d5a19..2e9630be39 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -157,7 +157,7 @@ public Builder withEventBus(final EventBus eventBus) { } public Runtime build() { - final ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader( + final ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, runtimeOptions); final ObjectFactorySupplier objectFactorySupplier = runtimeOptions.isMultiThreaded() diff --git a/core/src/test/java/io/cucumber/core/runtime/BackendServiceLoaderTest.java b/core/src/test/java/io/cucumber/core/runtime/BackendServiceLoaderTest.java index 1e09426730..94e46c6bdb 100644 --- a/core/src/test/java/io/cucumber/core/runtime/BackendServiceLoaderTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/BackendServiceLoaderTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; +import java.util.function.Supplier; + import static java.util.Collections.emptyList; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -14,21 +16,21 @@ class BackendServiceLoaderTest { + final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); + final Supplier classLoaderSupplier = this.getClass()::getClassLoader; + final ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoaderSupplier, + runtimeOptions); + final ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); + @Test void should_create_a_backend() { - RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); - ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); - BackendSupplier backendSupplier = new BackendServiceLoader(getClass()::getClassLoader, objectFactory); + BackendSupplier backendSupplier = new BackendServiceLoader(classLoaderSupplier, objectFactory); assertThat(backendSupplier.get().iterator().next(), is(notNullValue())); } @Test void should_throw_an_exception_when_no_backend_could_be_found() { - RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); - ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); - BackendServiceLoader backendSupplier = new BackendServiceLoader(getClass()::getClassLoader, objectFactory); + BackendServiceLoader backendSupplier = new BackendServiceLoader(classLoaderSupplier, objectFactory); Executable testMethod = () -> backendSupplier.get(emptyList()).iterator().next(); CucumberException actualThrown = assertThrows(CucumberException.class, testMethod); diff --git a/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java b/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java index a91e8d9f39..ba49f9c769 100644 --- a/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/SingletonRunnerSupplierTest.java @@ -22,7 +22,8 @@ class SingletonRunnerSupplierTest { void before() { Supplier classLoader = SingletonRunnerSupplier.class::getClassLoader; RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, + runtimeOptions); ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); BackendServiceLoader backendSupplier = new BackendServiceLoader(getClass()::getClassLoader, objectFactory); EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); diff --git a/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java b/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java index 3fde58c076..b0af1717c9 100644 --- a/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java +++ b/core/src/test/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplierTest.java @@ -31,7 +31,8 @@ class ThreadLocalRunnerSupplierTest { void before() { Supplier classLoader = ThreadLocalRunnerSupplierTest.class::getClassLoader; RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, + runtimeOptions); ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); BackendServiceLoader backendSupplier = new BackendServiceLoader(classLoader, objectFactory); eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); diff --git a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java index ccdc3ee15d..4810a9dba7 100644 --- a/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java +++ b/junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java @@ -44,7 +44,7 @@ public final class CucumberEngineExecutionContext implements EngineExecutionCont Supplier classLoader = CucumberEngineExecutionContext.class::getClassLoader; log.debug(() -> "Parsing options"); options = new CucumberEngineOptions(configurationParameters); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(options); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, options); EventBus bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier( classLoader, options); diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index d67b5a49eb..807417fe8c 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -163,7 +163,8 @@ public Cucumber(Class clazz) throws InitializationError { ExitStatus exitStatus = new ExitStatus(runtimeOptions); this.plugins.addPlugin(exitStatus); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, + runtimeOptions); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendSupplier backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier); TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier( diff --git a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java index 5a14b95506..e62a9af605 100644 --- a/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java +++ b/junit/src/test/java/io/cucumber/junit/FeatureRunnerTest.java @@ -94,7 +94,7 @@ void should_not_create_step_descriptions_by_default() { private FeatureRunner createFeatureRunner(Feature feature, JUnitOptions junitOption) { ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader( - RuntimeOptions.defaultOptions()); + getClass()::getClassLoader, RuntimeOptions.defaultOptions()); ObjectFactorySupplier objectFactory = new SingletonObjectFactorySupplier(objectFactoryServiceLoader); final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); diff --git a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java index d7eeea5ced..ca6fd4701f 100644 --- a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java +++ b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java @@ -92,7 +92,8 @@ public TestNGCucumberRunner(Class clazz) { Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); ExitStatus exitStatus = new ExitStatus(runtimeOptions); plugins.addPlugin(exitStatus); - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, + runtimeOptions); ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader); BackendServiceLoader backendSupplier = new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier); this.filters = new Filters(runtimeOptions);