Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Pass class loader to ServiceLoader.load invocations #2220

Merged
merged 1 commit into from
Feb 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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