diff --git a/impl/README.md b/impl/README.md index b65ef65c1..1c3d9dfc0 100644 --- a/impl/README.md +++ b/impl/README.md @@ -94,6 +94,7 @@ This SDK is modular by design—pull in only what you need: * **serverlessworkflow-impl-function** Function support. * **serverlessworkflow-impl-template-resolver** JaxRS URI template implementation. * **serverlessworkflow-impl-json** Json common utilities shared by all modules in this list + * **serverlessworkflow-impl-cron** Cron parsing * **serverlessworkflow-impl-http** HTTP `Call` task handler, based on Jax-RS client diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 1d3587aae..3ff80e8b8 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -24,10 +24,6 @@ com.github.f4b6a3 ulid-creator - - com.cronutils - cron-utils - org.junit.jupiter junit-jupiter-engine diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index c5da66600..b4b86e2bc 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -48,12 +48,15 @@ import io.serverlessworkflow.impl.resources.ResourceLoaderFactory; import io.serverlessworkflow.impl.resources.URITemplateResolver; import io.serverlessworkflow.impl.scheduler.AllStrategyCorrelationInfoFactory; +import io.serverlessworkflow.impl.scheduler.CronResolver; +import io.serverlessworkflow.impl.scheduler.CronResolverFactory; import io.serverlessworkflow.impl.scheduler.DefaultWorkflowScheduler; import io.serverlessworkflow.impl.scheduler.InMemoryAllStrategyCorrelationInfo; import io.serverlessworkflow.impl.scheduler.WorkflowScheduler; import io.serverlessworkflow.impl.schema.SchemaValidator; import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; import java.net.URI; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -68,6 +71,8 @@ import java.util.ServiceLoader.Provider; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -104,6 +109,7 @@ public class WorkflowApplication implements AutoCloseable { private final CloudEventPredicateFactory cloudEventPredicateFactory; private final AllStrategyCorrelationInfoFactory allStrategyCorrelationInfoFactory; private final WorkflowLifeCycleCloudEventFactory lifeCycleCloudEventFactory; + private final ScheduledExecutorService schedulerExecutorService; private WorkflowApplication(Builder builder) { this.taskFactory = builder.taskFactory; @@ -137,6 +143,7 @@ private WorkflowApplication(Builder builder) { this.cloudEventPredicateFactory = builder.cloudEventPredicateFactory; this.allStrategyCorrelationInfoFactory = builder.allStrategyCorrelationInfoFactory; this.lifeCycleCloudEventFactory = builder.lifeCycleCloudEventFactory; + this.schedulerExecutorService = builder.schedulerExecutorService; } public TaskExecutorFactory taskFactory() { @@ -259,6 +266,8 @@ public SchemaValidator getValidator(SchemaInline inline) { private CloudEventPredicateFactory cloudEventPredicateFactory; private AllStrategyCorrelationInfoFactory allStrategyCorrelationInfoFactory; private WorkflowLifeCycleCloudEventFactory lifeCycleCloudEventFactory; + private CronResolverFactory cronResolverFactory; + private ScheduledExecutorService schedulerExecutorService; private Builder() { ServiceLoader.load(NamedWorkflowAdditionalObject.class) @@ -413,6 +422,11 @@ public Builder withLifeCycleCloudEventFactory( return this; } + public Builder withCronResolverFactory(CronResolverFactory cronResolverFactory) { + this.cronResolverFactory = cronResolverFactory; + return this; + } + public WorkflowApplication build() { if (modelFactory == null) { @@ -452,8 +466,31 @@ public WorkflowApplication build() { if (idFactory == null) { idFactory = new MonotonicUlidWorkflowInstanceIdFactory(); } + if (scheduler == null) { - scheduler = new DefaultWorkflowScheduler(); + if (cronResolverFactory == null) { + cronResolverFactory = + loadFirst(CronResolverFactory.class) + .orElseGet( + () -> + new CronResolverFactory() { + private CronResolver emptyResolver = + new CronResolver() { + @Override + public Optional nextExecution() { + throw new UnsupportedOperationException( + "Missing CronResolverFactory, please add serverlessworkflow-impl-cron dependency to your classpath"); + } + }; + + @Override + public CronResolver parseCron(String cron) { + return emptyResolver; + } + }); + } + schedulerExecutorService = Executors.newSingleThreadScheduledExecutor(); + scheduler = new DefaultWorkflowScheduler(schedulerExecutorService, cronResolverFactory); } schedulerListener = new SchedulerListener(scheduler); listeners.add(schedulerListener); @@ -532,6 +569,9 @@ public void close() { } listenersByPriority.clear(); } + if (this.schedulerExecutorService != null) { + schedulerExecutorService.shutdownNow(); + } } public WorkflowPositionFactory positionFactory() { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronResolverFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronResolverFactory.java index 4c67f062b..5be71bbec 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronResolverFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronResolverFactory.java @@ -15,6 +15,8 @@ */ package io.serverlessworkflow.impl.scheduler; -public interface CronResolverFactory { +import io.serverlessworkflow.impl.ServicePriority; + +public interface CronResolverFactory extends ServicePriority { CronResolver parseCron(String cron); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/DefaultWorkflowScheduler.java b/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/DefaultWorkflowScheduler.java index f7fcf85a0..cc0eb907e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/DefaultWorkflowScheduler.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/DefaultWorkflowScheduler.java @@ -17,7 +17,6 @@ import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowInstance; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -28,10 +27,6 @@ public class DefaultWorkflowScheduler extends ExecutorServiceWorkflowScheduler { private final CronResolverFactory cronFactory; - public DefaultWorkflowScheduler() { - this(Executors.newSingleThreadScheduledExecutor(), new CronUtilsResolverFactory()); - } - public DefaultWorkflowScheduler( ScheduledExecutorService service, CronResolverFactory cronFactory) { super(service); diff --git a/impl/cron/pom.xml b/impl/cron/pom.xml new file mode 100644 index 000000000..53094624e --- /dev/null +++ b/impl/cron/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-impl + 8.0.0-SNAPSHOT + + serverlessworkflow-impl-cron + Serverless Workflow :: Impl :: Cron + + + com.cronutils + cron-utils + + + io.serverlessworkflow + serverlessworkflow-impl-core + + + \ No newline at end of file diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolver.java b/impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolver.java similarity index 91% rename from impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolver.java rename to impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolver.java index fcb7086b1..2ce36b311 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolver.java +++ b/impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolver.java @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.scheduler; +package io.serverlessworkflow.impl.cron; import com.cronutils.model.Cron; import com.cronutils.model.time.ExecutionTime; +import io.serverlessworkflow.impl.scheduler.CronResolver; import java.time.Duration; import java.time.ZonedDateTime; import java.util.Optional; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolverFactory.java b/impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolverFactory.java similarity index 83% rename from impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolverFactory.java rename to impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolverFactory.java index 3facca0e9..c5b9f5e42 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/scheduler/CronUtilsResolverFactory.java +++ b/impl/cron/src/main/java/io/serverlessworkflow/impl/cron/CronUtilsResolverFactory.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.scheduler; +package io.serverlessworkflow.impl.cron; import com.cronutils.model.CronType; import com.cronutils.model.definition.CronDefinitionBuilder; import com.cronutils.parser.CronParser; +import io.serverlessworkflow.impl.scheduler.CronResolver; +import io.serverlessworkflow.impl.scheduler.CronResolverFactory; -class CronUtilsResolverFactory implements CronResolverFactory { +public class CronUtilsResolverFactory implements CronResolverFactory { private final CronParser cronParser; diff --git a/impl/cron/src/main/resources/META-INF/services/io.serverlessworkflow.impl.scheduler.CronResolverFactory b/impl/cron/src/main/resources/META-INF/services/io.serverlessworkflow.impl.scheduler.CronResolverFactory new file mode 100644 index 000000000..846dc6696 --- /dev/null +++ b/impl/cron/src/main/resources/META-INF/services/io.serverlessworkflow.impl.scheduler.CronResolverFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.cron.CronUtilsResolverFactory \ No newline at end of file diff --git a/impl/jackson/pom.xml b/impl/jackson/pom.xml index 1a4176fdf..ec4b6d19c 100644 --- a/impl/jackson/pom.xml +++ b/impl/jackson/pom.xml @@ -36,5 +36,9 @@ io.serverlessworkflow serverlessworkflow-impl-lifecycle-events + + io.serverlessworkflow + serverlessworkflow-impl-cron + \ No newline at end of file diff --git a/impl/pom.xml b/impl/pom.xml index 8399933f5..8ff34aad6 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -130,6 +130,11 @@ serverlessworkflow-impl-a2a ${project.version} + + io.serverlessworkflow + serverlessworkflow-impl-cron + ${project.version} + net.thisptr jackson-jq @@ -231,5 +236,6 @@ grpc openapi-jackson a2a + cron diff --git a/impl/test/pom.xml b/impl/test/pom.xml index 165ef1a98..18f12414a 100644 --- a/impl/test/pom.xml +++ b/impl/test/pom.xml @@ -62,6 +62,10 @@ io.serverlessworkflow serverlessworkflow-impl-grpc + + io.serverlessworkflow + serverlessworkflow-impl-cron + org.glassfish.jersey.core jersey-client