Skip to content

Commit

Permalink
[Core] Pass class loader to ServiceLoader.load invocations
Browse files Browse the repository at this point in the history
Fixes: #2217, #2219
  • Loading branch information
mpkorstanje committed Feb 11, 2021
1 parent 31c1361 commit 4b261e6
Show file tree
Hide file tree
Showing 11 changed files with 37 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public BackendServiceLoader(

@Override
public Collection<? extends Backend> get() {
return get(ServiceLoader.load(BackendProviderService.class));
ClassLoader classLoader = classLoaderSupplier.get();
return get(ServiceLoader.load(BackendProviderService.class, classLoader));
}

Collection<? extends Backend> get(Iterable<BackendProviderService> serviceLoader) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClassLoader> classLoaderSupplier;
private final Options options;

@Deprecated
public ObjectFactoryServiceLoader(Options options) {
this(currentThread()::getContextClassLoader, options);
}

public ObjectFactoryServiceLoader(Supplier<ClassLoader> classLoaderSupplier, Options options) {
this.classLoaderSupplier = requireNonNull(classLoaderSupplier);
this.options = requireNonNull(options);
}

Expand All @@ -38,9 +47,9 @@ public ObjectFactoryServiceLoader(Options options) {
* @return an instance of {@link ObjectFactory}
*/
ObjectFactory loadObjectFactory() {
Class<? extends ObjectFactory> objectFactoryClass = this.options.getObjectFactoryClass();

final ServiceLoader<ObjectFactory> loader = ServiceLoader.load(ObjectFactory.class);
Class<? extends ObjectFactory> objectFactoryClass = options.getObjectFactoryClass();
ClassLoader classLoader = classLoaderSupplier.get();
ServiceLoader<ObjectFactory> loader = ServiceLoader.load(ObjectFactory.class, classLoader);
if (objectFactoryClass == null) {
return loadSingleObjectFactoryOrDefault(loader);

Expand All @@ -50,7 +59,7 @@ ObjectFactory loadObjectFactory() {
}

private static ObjectFactory loadSingleObjectFactoryOrDefault(ServiceLoader<ObjectFactory> loader) {
final Iterator<ObjectFactory> objectFactories = loader.iterator();
Iterator<ObjectFactory> objectFactories = loader.iterator();

ObjectFactory objectFactory;
if (objectFactories.hasNext()) {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/io/cucumber/core/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,21 +16,21 @@

class BackendServiceLoaderTest {

final RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions();
final Supplier<ClassLoader> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class SingletonRunnerSupplierTest {
void before() {
Supplier<ClassLoader> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class ThreadLocalRunnerSupplierTest {
void before() {
Supplier<ClassLoader> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public final class CucumberEngineExecutionContext implements EngineExecutionCont
Supplier<ClassLoader> 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);
Expand Down
3 changes: 2 additions & 1 deletion junit/src/main/java/io/cucumber/junit/Cucumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 4b261e6

Please sign in to comment.