Skip to content

Commit aa76421

Browse files
committed
[Core] Share object factory between backends
By sharing the object factory between different backends it becomes possible to use the same test context in different languages. This is very useful when mixing Kotlin and Java, or Java and Java 8. This also requires that the backend no longer manages the object factory life-cycle. To end a container and lookup have been extracted from the object factory. Closes #1117.
1 parent 0f44408 commit aa76421

33 files changed

+209
-144
lines changed

core/src/main/java/io/cucumber/core/backend/BackendProviderService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
public interface BackendProviderService {
77

8-
Backend create(ObjectFactory objectFactory, ResourceLoader resourceLoader, TypeRegistry typeRegistry);
8+
Backend create(Container container, ResourceLoader resourceLoader, TypeRegistry typeRegistry);
99

1010
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.cucumber.core.backend;
2+
3+
public interface Container extends Lookup {
4+
/**
5+
* Collects glue classes in the classpath. Called once on init.
6+
*
7+
* @param glueClass Glue class containing cucumber.api annotations (Before, Given, When, ...)
8+
* @return true if stepdefs and hooks in this class should be used, false if they should be ignored.
9+
*/
10+
boolean addClass(Class<?> glueClass);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.cucumber.core.backend;
2+
3+
public interface Lookup {
4+
/**
5+
* Provides the glue instances used to execute the current scenario.
6+
*
7+
* @param glueClass type of instance to be created.
8+
* @param <T> type of Glue class
9+
* @return new Glue instance of type T
10+
*/
11+
<T> T getInstance(Class<T> glueClass);
12+
}

core/src/main/java/io/cucumber/core/backend/ObjectFactory.java

+1-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* Minimal facade for Dependency Injection containers
55
*/
6-
public interface ObjectFactory {
6+
public interface ObjectFactory extends Container {
77

88
/**
99
* Instantiate glue code <b>before</b> scenario execution. Called once per scenario.
@@ -15,21 +15,4 @@ public interface ObjectFactory {
1515
*/
1616
void stop();
1717

18-
/**
19-
* Collects glue classes in the classpath. Called once on init.
20-
*
21-
* @param glueClass Glue class containing cucumber.api annotations (Before, Given, When, ...)
22-
* @return true if stepdefs and hooks in this class should be used, false if they should be ignored.
23-
*/
24-
boolean addClass(Class<?> glueClass);
25-
26-
/**
27-
* Provides the glue instances used to execute the current scenario. The instance can be prepared in
28-
* {@link #start()}.
29-
*
30-
* @param glueClass type of instance to be created.
31-
* @param <T> type of Glue class
32-
* @return new Glue instance of type T
33-
*/
34-
<T> T getInstance(Class<T> glueClass);
3518
}

core/src/main/java/io/cucumber/core/runner/Runner.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import gherkin.pickles.PickleTag;
99
import io.cucumber.core.backend.Backend;
1010
import io.cucumber.core.backend.HookDefinition;
11+
import io.cucumber.core.backend.ObjectFactory;
1112
import io.cucumber.core.event.EventBus;
1213
import io.cucumber.core.logging.Logger;
1314
import io.cucumber.core.logging.LoggerFactory;
@@ -27,11 +28,13 @@ public final class Runner {
2728
private final EventBus bus;
2829
private final Collection<? extends Backend> backends;
2930
private final RunnerOptions runnerOptions;
31+
private final ObjectFactory objectFactory;
3032

31-
public Runner(EventBus bus, Collection<? extends Backend> backends, RunnerOptions runnerOptions) {
33+
public Runner(EventBus bus, Collection<? extends Backend> backends, ObjectFactory objectFactory, RunnerOptions runnerOptions) {
3234
this.bus = bus;
3335
this.runnerOptions = runnerOptions;
3436
this.backends = backends;
37+
this.objectFactory = objectFactory;
3538
List<URI> gluePaths = runnerOptions.getGlue();
3639
log.debug("Loading glue from " + FixJava.join(gluePaths, ", "));
3740
for (Backend backend : backends) {
@@ -49,7 +52,6 @@ public void runPickle(PickleEvent pickle) {
4952
TestCase testCase = createTestCaseForPickle(pickle);
5053
testCase.run(bus);
5154
disposeBackendWorlds();
52-
glue.removeScenarioScopedGlue();
5355
}
5456

5557
public void reportStepDefinitions(StepDefinitionReporter stepDefinitionReporter) {
@@ -127,6 +129,7 @@ private List<HookTestStep> getBeforeStepHooks(List<PickleTag> tags) {
127129
}
128130

129131
private void buildBackendWorlds() {
132+
objectFactory.start();
130133
for (Backend backend : backends) {
131134
backend.buildWorld();
132135
}
@@ -136,5 +139,7 @@ private void disposeBackendWorlds() {
136139
for (Backend backend : backends) {
137140
backend.disposeWorld();
138141
}
142+
objectFactory.stop();
143+
glue.removeScenarioScopedGlue();
139144
}
140145
}

core/src/main/java/io/cucumber/core/runtime/BackendServiceLoader.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
import io.cucumber.core.backend.Backend;
55
import io.cucumber.core.backend.BackendProviderService;
66
import io.cucumber.core.backend.BackendSupplier;
7-
import io.cucumber.core.backend.ObjectFactory;
7+
import io.cucumber.core.backend.Container;
88
import io.cucumber.core.exception.CucumberException;
99
import io.cucumber.core.io.ClassFinder;
1010
import io.cucumber.core.io.ResourceLoader;
11-
import io.cucumber.core.options.Env;
1211
import io.cucumber.core.options.RuntimeOptions;
1312
import io.cucumber.core.reflection.Reflections;
1413
import io.cucumber.core.stepexpression.TypeRegistry;
@@ -19,8 +18,6 @@
1918
import java.util.Locale;
2019
import java.util.ServiceLoader;
2120

22-
import static io.cucumber.core.backend.ObjectFactoryLoader.loadObjectFactory;
23-
2421
/**
2522
* Supplies instances of {@link Backend} created by using a {@link ServiceLoader}
2623
* to locate instance of {@link BackendSupplier}.
@@ -30,11 +27,13 @@ public final class BackendServiceLoader implements BackendSupplier {
3027
private final ResourceLoader resourceLoader;
3128
private final ClassFinder classFinder;
3229
private final RuntimeOptions runtimeOptions;
30+
private final Container container;
3331

34-
public BackendServiceLoader(ResourceLoader resourceLoader, ClassFinder classFinder, RuntimeOptions runtimeOptions) {
32+
public BackendServiceLoader(ResourceLoader resourceLoader, ClassFinder classFinder, RuntimeOptions runtimeOptions, Container container) {
3533
this.resourceLoader = resourceLoader;
3634
this.classFinder = classFinder;
3735
this.runtimeOptions = runtimeOptions;
36+
this.container = container;
3837
}
3938

4039
@Override
@@ -54,8 +53,7 @@ private Collection<? extends Backend> loadBackends(Iterable<BackendProviderServi
5453
final TypeRegistry typeRegistry = createTypeRegistry();
5554
List<Backend> backends = new ArrayList<>();
5655
for (BackendProviderService backendProviderService : serviceLoader) {
57-
ObjectFactory objectFactory = loadObjectFactory(Env.INSTANCE.get(ObjectFactory.class.getName()));
58-
backends.add(backendProviderService.create(objectFactory, resourceLoader, typeRegistry));
56+
backends.add(backendProviderService.create(container, resourceLoader, typeRegistry));
5957
}
6058
return backends;
6159
}

core/src/main/java/io/cucumber/core/runtime/Runtime.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.cucumber.core.api.event.TestRunFinished;
1212
import io.cucumber.core.api.event.TestRunStarted;
1313
import io.cucumber.core.backend.BackendSupplier;
14+
import io.cucumber.core.backend.ObjectFactory;
1415
import io.cucumber.core.exception.CucumberException;
1516
import io.cucumber.core.event.EventBus;
1617
import io.cucumber.core.options.Env;
@@ -38,6 +39,7 @@
3839
import java.util.concurrent.TimeUnit;
3940

4041
import static io.cucumber.core.api.event.Result.SEVERITY;
42+
import static io.cucumber.core.backend.ObjectFactoryLoader.loadObjectFactory;
4143
import static java.util.Collections.emptyList;
4244
import static java.util.Collections.max;
4345
import static java.util.Collections.min;
@@ -189,10 +191,10 @@ public Runtime build() {
189191
final ClassFinder classFinder = this.classFinder != null
190192
? this.classFinder
191193
: new ResourceLoaderClassFinder(resourceLoader, this.classLoader);
192-
194+
ObjectFactory objectFactory = loadObjectFactory(Env.INSTANCE.get(ObjectFactory.class.getName()));
193195
final BackendSupplier backendSupplier = this.backendSupplier != null
194196
? this.backendSupplier
195-
: new BackendServiceLoader(resourceLoader, classFinder, runtimeOptions);
197+
: new BackendServiceLoader(resourceLoader, classFinder, runtimeOptions, objectFactory);
196198

197199
final Plugins plugins = new Plugins(new PluginFactory(), this.eventBus, runtimeOptions);
198200
for (final Plugin plugin : additionalPlugins) {
@@ -202,8 +204,8 @@ public Runtime build() {
202204
plugins.addPlugin(exitStatus);
203205

204206
final RunnerSupplier runnerSupplier = runtimeOptions.isMultiThreaded()
205-
? new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier)
206-
: new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier);
207+
? new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactory)
208+
: new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactory);
207209

208210
final ExecutorService executor = runtimeOptions.isMultiThreaded()
209211
? Executors.newFixedThreadPool(runtimeOptions.getThreads())

core/src/main/java/io/cucumber/core/runtime/SingletonRunnerSupplier.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.cucumber.core.runtime;
22

33
import io.cucumber.core.backend.BackendSupplier;
4+
import io.cucumber.core.backend.ObjectFactory;
45
import io.cucumber.core.event.EventBus;
56
import io.cucumber.core.options.RunnerOptions;
67
import io.cucumber.core.runner.Runner;
@@ -15,17 +16,19 @@ public final class SingletonRunnerSupplier implements RunnerSupplier {
1516
private final BackendSupplier backendSupplier;
1617
private final RunnerOptions runnerOptions;
1718
private final EventBus eventBus;
19+
private final ObjectFactory objectFactory;
1820
private Runner runner;
1921

2022

2123
public SingletonRunnerSupplier(
2224
RunnerOptions runnerOptions,
2325
EventBus eventBus,
24-
BackendSupplier backendSupplier
25-
) {
26+
BackendSupplier backendSupplier,
27+
ObjectFactory objectFactory) {
2628
this.backendSupplier = backendSupplier;
2729
this.runnerOptions = runnerOptions;
2830
this.eventBus = eventBus;
31+
this.objectFactory = objectFactory;
2932
}
3033

3134
@Override
@@ -37,7 +40,7 @@ public Runner get() {
3740
}
3841

3942
private Runner createRunner() {
40-
return new Runner(eventBus, backendSupplier.get(), runnerOptions);
43+
return new Runner(eventBus, backendSupplier.get(), objectFactory, runnerOptions);
4144
}
4245

4346
}

core/src/main/java/io/cucumber/core/runtime/ThreadLocalRunnerSupplier.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.cucumber.core.api.event.Event;
44
import io.cucumber.core.api.event.EventHandler;
55
import io.cucumber.core.backend.BackendSupplier;
6+
import io.cucumber.core.backend.ObjectFactory;
67
import io.cucumber.core.event.AbstractEventBus;
78
import io.cucumber.core.event.EventBus;
89
import io.cucumber.core.options.RunnerOptions;
@@ -18,22 +19,25 @@ public final class ThreadLocalRunnerSupplier implements RunnerSupplier {
1819
private final BackendSupplier backendSupplier;
1920
private final RunnerOptions runnerOptions;
2021
private final SynchronizedEventBus sharedEventBus;
22+
private final ObjectFactory objectFactory;
2123

2224
private final ThreadLocal<Runner> runners = new ThreadLocal<Runner>() {
2325
@Override
2426
protected Runner initialValue() {
2527
return createRunner();
2628
}
2729
};
30+
;
2831

2932
public ThreadLocalRunnerSupplier(
3033
RunnerOptions runnerOptions,
3134
EventBus sharedEventBus,
32-
BackendSupplier backendSupplier
33-
) {
35+
BackendSupplier backendSupplier,
36+
ObjectFactory objectFactory) {
3437
this.runnerOptions = runnerOptions;
3538
this.sharedEventBus = SynchronizedEventBus.synchronize(sharedEventBus);
3639
this.backendSupplier = backendSupplier;
40+
this.objectFactory = objectFactory;
3741
}
3842

3943
@Override
@@ -42,7 +46,7 @@ public Runner get() {
4246
}
4347

4448
private Runner createRunner() {
45-
return new Runner(new LocalEventBus(sharedEventBus), backendSupplier.get(), runnerOptions);
49+
return new Runner(new LocalEventBus(sharedEventBus), backendSupplier.get(), objectFactory, runnerOptions);
4650
}
4751

4852
private static final class LocalEventBus extends AbstractEventBus {

core/src/test/java/io/cucumber/core/backend/StubBackendProviderService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
public class StubBackendProviderService implements BackendProviderService {
1414
@Override
15-
public Backend create(ObjectFactory objectFactory, ResourceLoader resourceLoader, TypeRegistry typeRegistry) {
15+
public Backend create(Container container, ResourceLoader resourceLoader, TypeRegistry typeRegistry) {
1616
return new StubBackend();
1717
}
1818

core/src/test/java/io/cucumber/core/runner/HookTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.cucumber.core.backend.Glue;
55
import io.cucumber.core.backend.HookDefinition;
66
import io.cucumber.core.backend.Backend;
7+
import io.cucumber.core.backend.ObjectFactory;
78
import io.cucumber.core.event.EventBus;
89
import io.cucumber.core.io.MultiLoader;
910
import io.cucumber.core.options.Env;
@@ -47,6 +48,7 @@ public class HookTest {
4748
public void after_hooks_execute_before_objects_are_disposed() throws Throwable {
4849

4950
Backend backend = mock(Backend.class);
51+
ObjectFactory objectFactory = mock(ObjectFactory.class);
5052
final HookDefinition hook = mock(HookDefinition.class);
5153
when(hook.matches(ArgumentMatchers.<PickleTag>anyCollection())).thenReturn(true);
5254

@@ -59,7 +61,7 @@ public Object answer(InvocationOnMock invocation) {
5961
}
6062
}).when(backend).loadGlue(any(Glue.class), ArgumentMatchers.<URI>anyList());
6163

62-
Runner runner = new Runner(bus, Collections.singleton(backend), runtimeOptions);
64+
Runner runner = new Runner(bus, Collections.singleton(backend), objectFactory, runtimeOptions);
6365

6466
runner.runPickle(pickleEvent);
6567

0 commit comments

Comments
 (0)