From 67b6ea2b80f433b9e5a0192bda68cee14aa30dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 7 Aug 2025 14:18:35 +0200 Subject: [PATCH 1/3] Add a new option to supply a custom UuidGenerator when building runtime Currently the UuidGenerator is looked up by SPI when creating the event bus. This requires the UuidGenerator to be part of the classloader what might not always be the case. This now adds an option to set an actual instance for the UuidGenerator and uses that in case it is specified. --- .../io/cucumber/core/runtime/Runtime.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java index 1a17276692..81b189fc1a 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -3,6 +3,7 @@ import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; +import io.cucumber.core.eventbus.UuidGenerator; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.logging.Logger; @@ -119,6 +120,7 @@ public static class Builder { private BackendSupplier backendSupplier; private FeatureSupplier featureSupplier; private List additionalPlugins = emptyList(); + private UuidGenerator uuidGenerator; private Builder() { } @@ -142,6 +144,11 @@ public Builder withFeatureSupplier(final FeatureSupplier featureSupplier) { this.featureSupplier = featureSupplier; return this; } + + public Builder withFeatureSupplier(final UuidGenerator uuidGenerator) { + this.uuidGenerator = uuidGenerator; + return this; + } public Builder withAdditionalPlugins(final Plugin... plugins) { this.additionalPlugins = Arrays.asList(plugins); @@ -173,11 +180,15 @@ public Runtime build() { plugins.addPlugin(exitStatus); if (this.eventBus == null) { - final UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader( - classLoader, - runtimeOptions); - this.eventBus = new TimeServiceEventBus(Clock.systemUTC(), - uuidGeneratorServiceLoader.loadUuidGenerator()); + UuidGenerator generator; + if (uuidGenerator == null) { + final UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader( + classLoader, runtimeOptions); + generator = uuidGeneratorServiceLoader.loadUuidGenerator(); + } else { + generator = uuidGenerator; + } + this.eventBus = new TimeServiceEventBus(Clock.systemUTC(), generator); } final EventBus eventBus = synchronize(this.eventBus); From 3edd8d0eb63702b25d7ee0727fd5837c07315354 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 17 Aug 2025 15:54:21 +0200 Subject: [PATCH 2/3] Refactor and add withObjectFactorySupplier --- .../io/cucumber/core/runtime/Runtime.java | 138 +++++++++++------- 1 file changed, 87 insertions(+), 51 deletions(-) diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java index 81b189fc1a..9d9edad340 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -1,9 +1,9 @@ package io.cucumber.core.runtime; import io.cucumber.core.eventbus.EventBus; +import io.cucumber.core.eventbus.UuidGenerator; import io.cucumber.core.feature.FeatureParser; import io.cucumber.core.filter.Filters; -import io.cucumber.core.eventbus.UuidGenerator; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.gherkin.Pickle; import io.cucumber.core.logging.Logger; @@ -118,106 +118,142 @@ public static class Builder { private Supplier classLoader = ClassLoaders::getDefaultClassLoader; private RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); private BackendSupplier backendSupplier; + private ObjectFactorySupplier objectFactorySupplier; private FeatureSupplier featureSupplier; private List additionalPlugins = emptyList(); - private UuidGenerator uuidGenerator; + private Supplier uuidGeneratorSupplier; private Builder() { } - public Builder withRuntimeOptions(final RuntimeOptions runtimeOptions) { + public Builder withRuntimeOptions(RuntimeOptions runtimeOptions) { this.runtimeOptions = runtimeOptions; return this; } - public Builder withClassLoader(final Supplier classLoader) { + public Builder withClassLoader(Supplier classLoader) { this.classLoader = classLoader; return this; } - public Builder withBackendSupplier(final BackendSupplier backendSupplier) { + public Builder withBackendSupplier(BackendSupplier backendSupplier) { this.backendSupplier = backendSupplier; return this; } - public Builder withFeatureSupplier(final FeatureSupplier featureSupplier) { + public Builder withObjectFactorySupplier(ObjectFactorySupplier objectFactorySupplier) { + this.objectFactorySupplier = objectFactorySupplier; + return this; + } + + public Builder withFeatureSupplier(FeatureSupplier featureSupplier) { this.featureSupplier = featureSupplier; return this; } - - public Builder withFeatureSupplier(final UuidGenerator uuidGenerator) { - this.uuidGenerator = uuidGenerator; + + public Builder withUuidGeneratorSupplier(Supplier uuidGenerator) { + this.uuidGeneratorSupplier = uuidGenerator; return this; } - public Builder withAdditionalPlugins(final Plugin... plugins) { + public Builder withAdditionalPlugins(Plugin... plugins) { this.additionalPlugins = Arrays.asList(plugins); return this; } - public Builder withEventBus(final EventBus eventBus) { + public Builder withEventBus(EventBus eventBus) { this.eventBus = eventBus; return this; } public Runtime build() { - final ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, - runtimeOptions); + EventBus eventBus = synchronize(createEventBus()); + ExitStatus exitStatus = createPluginsAndExitStatus(eventBus); + RunnerSupplier runnerSupplier = createRunnerSupplier(eventBus); + CucumberExecutionContext context = new CucumberExecutionContext(eventBus, exitStatus, runnerSupplier); + Predicate filter = new Filters(runtimeOptions); + int limit = runtimeOptions.getLimitCount(); + FeatureSupplier featureSupplier = createFeatureSupplier(); + ExecutorService executor = createExecutorService(); + PickleOrder pickleOrder = runtimeOptions.getPickleOrder(); + return new Runtime(exitStatus, context, filter, limit, featureSupplier, executor, pickleOrder); + } + + private ExitStatus createPluginsAndExitStatus(EventBus eventBus) { + Plugins plugins = createPlugins(); + ExitStatus exitStatus = new ExitStatus(runtimeOptions); + plugins.addPlugin(exitStatus); - final ObjectFactorySupplier objectFactorySupplier = runtimeOptions.isMultiThreaded() + if (runtimeOptions.isMultiThreaded()) { + plugins.setSerialEventBusOnEventListenerPlugins(eventBus); + } else { + plugins.setEventBusOnEventListenerPlugins(eventBus); + } + return exitStatus; + } + + + private RunnerSupplier createRunnerSupplier(EventBus eventBus) { + ObjectFactorySupplier objectFactorySupplier = createObjectFactorySupplier(); + BackendSupplier backendSupplier = createBackendSupplier(objectFactorySupplier); + return runtimeOptions.isMultiThreaded() + ? new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier) + : new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier); + } + + private ObjectFactorySupplier createObjectFactorySupplier() { + if (this.objectFactorySupplier != null) { + return objectFactorySupplier; + } + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, runtimeOptions); + return runtimeOptions.isMultiThreaded() ? new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader) : new SingletonObjectFactorySupplier(objectFactoryServiceLoader); + } - final BackendSupplier backendSupplier = this.backendSupplier != null + private BackendSupplier createBackendSupplier(ObjectFactorySupplier objectFactorySupplier) { + return this.backendSupplier != null ? this.backendSupplier : new BackendServiceLoader(this.classLoader, objectFactorySupplier); + } - final Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); - for (final Plugin plugin : additionalPlugins) { - plugins.addPlugin(plugin); - } - final ExitStatus exitStatus = new ExitStatus(runtimeOptions); - plugins.addPlugin(exitStatus); - - if (this.eventBus == null) { - UuidGenerator generator; - if (uuidGenerator == null) { - final UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader( - classLoader, runtimeOptions); - generator = uuidGeneratorServiceLoader.loadUuidGenerator(); - } else { - generator = uuidGenerator; - } - this.eventBus = new TimeServiceEventBus(Clock.systemUTC(), generator); + private EventBus createEventBus() { + if (this.eventBus != null) { + return this.eventBus; } - final EventBus eventBus = synchronize(this.eventBus); + UuidGenerator uuidGenerator = createUuidGenerator(); + return new TimeServiceEventBus(Clock.systemUTC(), uuidGenerator); + } - if (runtimeOptions.isMultiThreaded()) { - plugins.setSerialEventBusOnEventListenerPlugins(eventBus); + private UuidGenerator createUuidGenerator() { + if (uuidGeneratorSupplier != null) { + return uuidGeneratorSupplier.get(); } else { - plugins.setEventBusOnEventListenerPlugins(eventBus); + return new UuidGeneratorServiceLoader(classLoader, runtimeOptions).loadUuidGenerator(); } + } - final RunnerSupplier runnerSupplier = runtimeOptions.isMultiThreaded() - ? new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier) - : new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier); + private FeatureSupplier createFeatureSupplier() { + if (this.featureSupplier != null) { + return this.featureSupplier; + } + FeatureParser parser = new FeatureParser(eventBus::generateId); + return new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); + } - final ExecutorService executor = runtimeOptions.isMultiThreaded() + private ExecutorService createExecutorService() { + return runtimeOptions.isMultiThreaded() ? Executors.newFixedThreadPool(runtimeOptions.getThreads(), new CucumberThreadFactory()) : new SameThreadExecutorService(); + } - final FeatureParser parser = new FeatureParser(eventBus::generateId); - - final FeatureSupplier featureSupplier = this.featureSupplier != null - ? this.featureSupplier - : new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser); - - final Predicate filter = new Filters(runtimeOptions); - final int limit = runtimeOptions.getLimitCount(); - final PickleOrder pickleOrder = runtimeOptions.getPickleOrder(); - final CucumberExecutionContext context = new CucumberExecutionContext(eventBus, exitStatus, runnerSupplier); - return new Runtime(exitStatus, context, filter, limit, featureSupplier, executor, pickleOrder); + private Plugins createPlugins() { + Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); + for (Plugin plugin : additionalPlugins) { + plugins.addPlugin(plugin); + } + return plugins; } } From 6c5cca292551081f5a31f0f9e4173751752757e5 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 17 Aug 2025 15:59:04 +0200 Subject: [PATCH 3/3] Refactor and add withObjectFactorySupplier --- .../main/java/io/cucumber/core/runtime/Runtime.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java index 9d9edad340..87fdf4503e 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -173,12 +173,12 @@ public Runtime build() { CucumberExecutionContext context = new CucumberExecutionContext(eventBus, exitStatus, runnerSupplier); Predicate filter = new Filters(runtimeOptions); int limit = runtimeOptions.getLimitCount(); - FeatureSupplier featureSupplier = createFeatureSupplier(); + FeatureSupplier featureSupplier = createFeatureSupplier(eventBus); ExecutorService executor = createExecutorService(); PickleOrder pickleOrder = runtimeOptions.getPickleOrder(); return new Runtime(exitStatus, context, filter, limit, featureSupplier, executor, pickleOrder); } - + private ExitStatus createPluginsAndExitStatus(EventBus eventBus) { Plugins plugins = createPlugins(); ExitStatus exitStatus = new ExitStatus(runtimeOptions); @@ -192,7 +192,6 @@ private ExitStatus createPluginsAndExitStatus(EventBus eventBus) { return exitStatus; } - private RunnerSupplier createRunnerSupplier(EventBus eventBus) { ObjectFactorySupplier objectFactorySupplier = createObjectFactorySupplier(); BackendSupplier backendSupplier = createBackendSupplier(objectFactorySupplier); @@ -200,12 +199,13 @@ private RunnerSupplier createRunnerSupplier(EventBus eventBus) { ? new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier) : new SingletonRunnerSupplier(runtimeOptions, eventBus, backendSupplier, objectFactorySupplier); } - + private ObjectFactorySupplier createObjectFactorySupplier() { if (this.objectFactorySupplier != null) { return objectFactorySupplier; } - ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, runtimeOptions); + ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, + runtimeOptions); return runtimeOptions.isMultiThreaded() ? new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader) : new SingletonObjectFactorySupplier(objectFactoryServiceLoader); @@ -233,7 +233,7 @@ private UuidGenerator createUuidGenerator() { } } - private FeatureSupplier createFeatureSupplier() { + private FeatureSupplier createFeatureSupplier(EventBus eventBus) { if (this.featureSupplier != null) { return this.featureSupplier; } @@ -247,7 +247,6 @@ private ExecutorService createExecutorService() { : new SameThreadExecutorService(); } - private Plugins createPlugins() { Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions); for (Plugin plugin : additionalPlugins) {