├── _config.yml ├── samples ├── sample08 │ ├── README.md │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── net │ │ │ │ └── jworkflow │ │ │ │ └── sample08 │ │ │ │ ├── MyData.java │ │ │ │ ├── steps │ │ │ │ ├── Hello.java │ │ │ │ ├── Task1.java │ │ │ │ ├── Task3.java │ │ │ │ ├── Goodbye.java │ │ │ │ ├── UndoTask1.java │ │ │ │ ├── UndoTask2.java │ │ │ │ ├── UndoTask3.java │ │ │ │ ├── Task2.java │ │ │ │ ├── PrintMessage.java │ │ │ │ └── AddNumbers.java │ │ │ │ └── Main.java │ │ │ └── resources │ │ │ ├── workflow1.json │ │ │ ├── workflow3.json │ │ │ ├── workflow2.json │ │ │ ├── workflow4.json │ │ │ └── workflow5.json │ ├── nbactions.xml │ └── pom.xml ├── .gitignore ├── sample01 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample01 │ │ │ ├── steps │ │ │ ├── Hello.java │ │ │ └── Goodbye.java │ │ │ ├── HelloWorkflow.java │ │ │ └── Main.java │ ├── nbactions.xml │ ├── README.md │ └── pom.xml ├── sample02 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample02 │ │ │ ├── MyData.java │ │ │ ├── steps │ │ │ ├── DisplayAnswer.java │ │ │ └── AddNumbers.java │ │ │ ├── DataWorkflow.java │ │ │ └── Main.java │ ├── nbactions.xml │ ├── pom.xml │ └── README.md ├── sample03 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample03 │ │ │ ├── MyData.java │ │ │ ├── steps │ │ │ ├── Hello.java │ │ │ └── DisplayAnswer.java │ │ │ ├── EventsWorkflow.java │ │ │ └── Main.java │ ├── nbactions.xml │ ├── README.md │ └── pom.xml ├── sample04 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample04 │ │ │ ├── MyData.java │ │ │ ├── steps │ │ │ ├── Hello.java │ │ │ ├── Goodbye.java │ │ │ └── DoSomething.java │ │ │ ├── ForeachWorkflow.java │ │ │ └── Main.java │ ├── nbactions.xml │ ├── pom.xml │ └── README.md ├── sample05 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample05 │ │ │ ├── MyData.java │ │ │ ├── steps │ │ │ ├── Hello.java │ │ │ ├── Goodbye.java │ │ │ └── IncrementValue.java │ │ │ ├── Main.java │ │ │ └── WhileWorkflow.java │ ├── nbactions.xml │ ├── README.md │ └── pom.xml ├── sample06 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample06 │ │ │ ├── MyData.java │ │ │ ├── steps │ │ │ ├── MakeDecision.java │ │ │ ├── Hello.java │ │ │ ├── Goodbye.java │ │ │ └── PrintMessage.java │ │ │ ├── Main.java │ │ │ └── IfWorkflow.java │ ├── pom.xml │ ├── README.md │ └── nbactions.xml ├── sample07 │ ├── .gitignore │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ └── sample07 │ │ │ ├── steps │ │ │ ├── Task1.java │ │ │ ├── Task3.java │ │ │ ├── UndoTask1.java │ │ │ ├── UndoTask2.java │ │ │ ├── UndoTask3.java │ │ │ └── Task2.java │ │ │ ├── Main.java │ │ │ └── CompensatingWorkflow.java │ ├── README.md │ ├── pom.xml │ └── nbactions.xml └── pom.xml ├── scratchpad ├── .gitignore ├── nbactions.xml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── jworkflow │ │ └── scratchpad │ │ ├── TestData.java │ │ └── Main.java └── pom.xml ├── jworkflow.kernel ├── .gitignore ├── src │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── jworkflow │ │ │ ├── kernel │ │ │ ├── models │ │ │ │ ├── QueueType.java │ │ │ │ ├── ExecutionPipelineResult.java │ │ │ │ ├── ErrorBehavior.java │ │ │ │ ├── WorkflowStatus.java │ │ │ │ ├── Event.java │ │ │ │ ├── ScheduleStepData.java │ │ │ │ ├── EventSubscription.java │ │ │ │ ├── ControlStepData.java │ │ │ │ ├── WorkflowExecutorResult.java │ │ │ │ ├── PointerStatus.java │ │ │ │ ├── StepOutcome.java │ │ │ │ ├── ExecutionPointer.java │ │ │ │ ├── WorkflowStepInline.java │ │ │ │ ├── WorkflowDefinition.java │ │ │ │ ├── StepExecutionContext.java │ │ │ │ ├── ExecutionPointerCollection.java │ │ │ │ └── WorkflowInstance.java │ │ │ ├── interfaces │ │ │ │ ├── BackgroundService.java │ │ │ │ ├── StepBuilderConsumer.java │ │ │ │ ├── LockService.java │ │ │ │ ├── StepFieldConsumer.java │ │ │ │ ├── WorkflowBuilderConsumer.java │ │ │ │ ├── Workflow.java │ │ │ │ ├── ControlStepBuilder.java │ │ │ │ ├── StepBody.java │ │ │ │ ├── WorkflowExecutor.java │ │ │ │ ├── QueueService.java │ │ │ │ ├── ParallelStepBuilder.java │ │ │ │ ├── StepExecutionConsumer.java │ │ │ │ ├── StepErrorHandler.java │ │ │ │ ├── WorkflowRegistry.java │ │ │ │ ├── ExecutionResultProcessor.java │ │ │ │ ├── ExecutionPointerFactory.java │ │ │ │ ├── StepOutcomeBuilder.java │ │ │ │ ├── WorkflowBuilder.java │ │ │ │ ├── WorkflowHost.java │ │ │ │ └── PersistenceService.java │ │ │ ├── exceptions │ │ │ │ └── CorruptPersistenceDataException.java │ │ │ ├── errorhandlers │ │ │ │ ├── SuspendHandler.java │ │ │ │ ├── TerminateHandler.java │ │ │ │ └── RetryHandler.java │ │ │ ├── services │ │ │ │ ├── SingleNodeLockService.java │ │ │ │ ├── SingleNodeQueueService.java │ │ │ │ ├── QueueWorker.java │ │ │ │ ├── DefaultExecutionPointerFactory.java │ │ │ │ ├── PollThread.java │ │ │ │ ├── WorkflowWorker.java │ │ │ │ └── DefaultWorkflowRegistry.java │ │ │ └── builders │ │ │ │ ├── BaseWorkflowBuilder.java │ │ │ │ ├── DefaultParallelStepBuilder.java │ │ │ │ ├── DefaultStepOutcomeBuilder.java │ │ │ │ └── DefaultWorkflowBuilder.java │ │ │ ├── definitionstorage │ │ │ ├── models │ │ │ │ ├── WorkflowDefinitionLoadException.java │ │ │ │ ├── DefinitionSource.java │ │ │ │ └── StepSource.java │ │ │ └── services │ │ │ │ └── DefinitionLoader.java │ │ │ └── primitives │ │ │ ├── ConsumerStep.java │ │ │ ├── Delay.java │ │ │ ├── SagaContainer.java │ │ │ ├── Recur.java │ │ │ ├── WaitFor.java │ │ │ ├── Sequence.java │ │ │ ├── Foreach.java │ │ │ ├── CancellableStep.java │ │ │ ├── Schedule.java │ │ │ ├── If.java │ │ │ └── While.java │ └── test │ │ └── java │ │ └── net │ │ └── jworkflow │ │ └── kernel │ │ ├── services │ │ ├── abstractions │ │ │ ├── TestDataClass.java │ │ │ └── WorkflowTest.java │ │ └── test │ │ │ └── MemoryPersistenceServiceTest.java │ │ └── scenarios │ │ ├── Scenario.java │ │ ├── BasicWorkflowScenario.java │ │ ├── EventScenario.java │ │ ├── ForeachScenario.java │ │ ├── WhileScenario.java │ │ ├── SagaScenario.java │ │ └── DataIOScenario.java └── nb-configuration.xml ├── jworkflow.providers.aws ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── net │ │ └── jworkflow │ │ └── providers │ │ └── aws │ │ ├── DynamoDBProvisioner.java │ │ ├── SQSProvider.java │ │ ├── DynamoDBLockProvider.java │ │ ├── DynamoDBPersistenceProvider.java │ │ └── SQSQueueService.java └── README.md ├── jworkflow.providers.redis ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── net │ │ └── jworkflow │ │ └── providers │ │ └── redis │ │ ├── RedisLockServiceProvider.java │ │ ├── RedisQueueServiceProvider.java │ │ ├── RedisLockService.java │ │ └── RedisQueueService.java └── README.md ├── jworkflow.providers.mongodb ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── net │ │ └── jworkflow │ │ └── providers │ │ └── mongodb │ │ └── MongoPersistenceServiceProvider.java └── README.md ├── jworkflow.providers.rabbitmq ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── net │ │ └── jworkflow │ │ └── providers │ │ └── rabbitmq │ │ ├── RabbitMQProvider.java │ │ └── RabbitMQQueueService.java └── README.md ├── .gitignore ├── docs ├── 04-error-handling.md ├── 07-schedule.md ├── 03-events.md ├── readme.md ├── 02-data.md └── 05-control-structures.md ├── .travis.yml └── LICENSE.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-modernist -------------------------------------------------------------------------------- /samples/sample08/README.md: -------------------------------------------------------------------------------- 1 | # JSON samples 2 | 3 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /scratchpad/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.kernel/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample01/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample02/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample03/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample04/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample05/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample06/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample07/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /samples/sample08/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.providers.redis/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.providers.mongodb/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.providers.rabbitmq/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.project 3 | /.settings 4 | /.classpath 5 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/QueueType.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public enum QueueType { WORKFLOW, EVENT } 4 | -------------------------------------------------------------------------------- /samples/sample03/src/main/java/net/jworkflow/sample03/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample03; 2 | 3 | public class MyData { 4 | public Object value1; 5 | } 6 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05; 2 | 3 | public class MyData { 4 | public int value1; 5 | } 6 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06; 2 | 3 | public class MyData { 4 | public int value1; 5 | } 6 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04; 2 | 3 | public class MyData { 4 | public String[] value1; 5 | } 6 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ExecutionPipelineResult.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public enum ExecutionPipelineResult { NEXT, DEFER } 4 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ErrorBehavior.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public enum ErrorBehavior { RETRY, SUSPEND, TERMINATE, COMPENSATE } 4 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/WorkflowStatus.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public enum WorkflowStatus { RUNNABLE, SUSPENDED, COMPLETE, TERMINATED } 4 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/BackgroundService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | public interface BackgroundService { 4 | void start(); 5 | void stop(); 6 | } 7 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/exceptions/CorruptPersistenceDataException.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.exceptions; 2 | 3 | public class CorruptPersistenceDataException extends Exception { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepBuilderConsumer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface StepBuilderConsumer extends Consumer { } -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/services/abstractions/TestDataClass.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services.abstractions; 2 | 3 | public class TestDataClass { 4 | public int Value1; 5 | public int Value2; 6 | public int Value3; 7 | } 8 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08; 2 | 3 | public class MyData { 4 | public int value1; 5 | public int value2; 6 | public int value3; 7 | 8 | public Object[] collection1; 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/LockService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | public interface LockService { 4 | boolean acquireLock(String id); 5 | void releaseLock(String id); 6 | void start(); 7 | void stop(); 8 | } 9 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepFieldConsumer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.function.BiConsumer; 4 | 5 | public interface StepFieldConsumer extends BiConsumer { 6 | 7 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/WorkflowBuilderConsumer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface WorkflowBuilderConsumer extends Consumer> { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /samples/sample02/src/main/java/net/jworkflow/sample02/MyData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample02; 2 | 3 | import java.io.Serializable; 4 | 5 | public class MyData implements Serializable { 6 | public int value1; 7 | public int value2; 8 | public int value3; 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/Workflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | public interface Workflow { 4 | String getId(); 5 | Class getDataType(); 6 | int getVersion(); 7 | void build(WorkflowBuilder builder); 8 | } 9 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/ControlStepBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | public interface ControlStepBuilder { 4 | 5 | StepBuilder Do(WorkflowBuilderConsumer consumer); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/definitionstorage/models/WorkflowDefinitionLoadException.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.definitionstorage.models; 2 | 3 | public class WorkflowDefinitionLoadException extends Exception { 4 | public WorkflowDefinitionLoadException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/definitionstorage/services/DefinitionLoader.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.definitionstorage.services; 2 | 3 | import net.jworkflow.kernel.models.WorkflowDefinition; 4 | 5 | public interface DefinitionLoader { 6 | 7 | WorkflowDefinition loadFromJson(String json) throws Exception; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepBody.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.ExecutionResult; 4 | import net.jworkflow.kernel.models.StepExecutionContext; 5 | 6 | public interface StepBody { 7 | ExecutionResult run(StepExecutionContext context) throws Exception; 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /jworkflow.providers.mongodb/target/ 2 | /sample03/nbproject/private/ 3 | /scratchpad/target/ 4 | /sample03/target/ 5 | /sample04/target/ 6 | /sample05/target/ 7 | /sample06/target/ 8 | /sample07/target/ 9 | /sample08/target/ 10 | /jworkflow.providers.redis/target/ 11 | /jworkflow.providers.aws/target/ 12 | /jworkflow.providers.rabbitmq/target/ -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/WorkflowExecutor.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.WorkflowExecutorResult; 4 | import net.jworkflow.kernel.models.WorkflowInstance; 5 | 6 | public interface WorkflowExecutor { 7 | WorkflowExecutorResult execute(WorkflowInstance workflow); 8 | } 9 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/QueueService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.QueueType; 4 | 5 | public interface QueueService { 6 | void queueForProcessing(QueueType type, String id); 7 | String dequeueForProcessing(QueueType type); 8 | boolean isDequeueBlocking(); 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/Event.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.util.Date; 4 | 5 | public class Event { 6 | public String id; 7 | public String eventName; 8 | public String eventKey; 9 | public Object eventData; 10 | public Date eventTimeUtc; 11 | public boolean isProcessed; 12 | } 13 | -------------------------------------------------------------------------------- /samples/sample08/src/main/resources/workflow1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-workflow", 3 | "version": 1, 4 | "steps": [ 5 | { 6 | "id": "step1", 7 | "stepType": "net.jworkflow.sample08.steps.Hello", 8 | "nextStepId": "step2" 9 | }, 10 | { 11 | "id": "step2", 12 | "stepType": "net.jworkflow.sample08.steps.Goodbye" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ScheduleStepData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public class ScheduleStepData { 4 | 5 | public boolean elapsed; 6 | 7 | public ScheduleStepData() { 8 | } 9 | 10 | public ScheduleStepData(boolean elapsed) { 11 | this.elapsed = elapsed; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/ParallelStepBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.primitives.Sequence; 4 | 5 | public interface ParallelStepBuilder { 6 | ParallelStepBuilder Do(WorkflowBuilderConsumer consumer); 7 | StepBuilder Join(); 8 | } 9 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/EventSubscription.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.util.Date; 4 | 5 | public class EventSubscription { 6 | public String id; 7 | public String workflowId; 8 | public int stepId; 9 | public String eventName; 10 | public String eventKey; 11 | public Date subscribeAsOfUtc; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ControlStepData.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public class ControlStepData { 4 | 5 | public boolean childrenActive; 6 | 7 | public ControlStepData() { 8 | } 9 | 10 | public ControlStepData(boolean childrenActive) { 11 | this.childrenActive = childrenActive; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepExecutionConsumer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.ExecutionResult; 4 | import net.jworkflow.kernel.models.StepExecutionContext; 5 | import java.util.function.Function; 6 | 7 | public interface StepExecutionConsumer extends Function { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/src/main/java/net/jworkflow/providers/aws/DynamoDBProvisioner.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.aws; 2 | 3 | import software.amazon.awssdk.awscore.exception.AwsServiceException; 4 | import software.amazon.awssdk.core.exception.SdkClientException; 5 | 6 | public interface DynamoDBProvisioner { 7 | 8 | void ensureTables() throws AwsServiceException, SdkClientException; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/WorkflowExecutorResult.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class WorkflowExecutorResult { 7 | public boolean requeue; 8 | public List subscriptions; 9 | 10 | public WorkflowExecutorResult() { 11 | this.subscriptions = new ArrayList<>(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/steps/MakeDecision.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class MakeDecision implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | return ExecutionResult.outcome(1); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/04-error-handling.md: -------------------------------------------------------------------------------- 1 | ### Error handling 2 | 3 | Each step can be configured with it's own error handling behavior, it can be retried at a later time, suspend the workflow or terminate the workflow. 4 | 5 | ```java 6 | public void build(WorkflowBuilder builder) { 7 | builder 8 | .startsWith(Hello.class) 9 | .then(DoSomething.class) 10 | .onError(ErrorBehavior.RETRY, Duration.ofMinutes(30)); 11 | } 12 | ``` 13 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepErrorHandler.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.Queue; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public interface StepErrorHandler { 7 | ErrorBehavior getErrorBehavior(); 8 | void handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Queue bubleupQueue); 9 | } 10 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/WorkflowRegistry.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.WorkflowDefinition; 4 | 5 | public interface WorkflowRegistry { 6 | void registerWorkflow(Workflow workflow) throws Exception; 7 | void registerWorkflow(WorkflowDefinition definition) throws Exception; 8 | WorkflowDefinition getDefinition(String workflowId, int version); 9 | } 10 | -------------------------------------------------------------------------------- /samples/sample01/src/main/java/net/jworkflow/sample01/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample01.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello world"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello there"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello there"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello there"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/Task1.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task1 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Doing Task 1"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/Task3.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task3 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Doing Task 3"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello there"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/Task1.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task1 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Doing Task 1"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/Task3.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task3 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Doing Task 3"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample03/src/main/java/net/jworkflow/sample03/steps/Hello.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample03.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Hello implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Hello world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample01/src/main/java/net/jworkflow/sample01/steps/Goodbye.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample01.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Goodbye implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Goodbye world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/steps/Goodbye.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Goodbye implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Goodbye world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/steps/Goodbye.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Goodbye implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Goodbye world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/steps/Goodbye.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Goodbye implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Goodbye world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/UndoTask1.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask1 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 1"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/UndoTask2.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask2 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 2"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/UndoTask3.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask3 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 3"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/Goodbye.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Goodbye implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Goodbye world"); 11 | return ExecutionResult.next(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/UndoTask1.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask1 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 1"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/UndoTask2.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask2 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 2"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/UndoTask3.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class UndoTask3 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Undoing Task 3"); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/steps/Task2.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task2 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) throws Exception { 10 | System.out.println("Doing Task 2"); 11 | throw new Exception("Explode!!!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/Task2.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class Task2 implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) throws Exception { 10 | System.out.println("Doing Task 2"); 11 | throw new Exception("Explode!!!"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/steps/DoSomething.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class DoSomething implements StepBody { 7 | 8 | @Override 9 | public ExecutionResult run(StepExecutionContext context) { 10 | System.out.println("Doing something with " + context.getItem()); 11 | return ExecutionResult.next(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/steps/PrintMessage.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class PrintMessage implements StepBody { 7 | 8 | public String message; 9 | 10 | @Override 11 | public ExecutionResult run(StepExecutionContext context) { 12 | System.out.println(message); 13 | return ExecutionResult.next(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/PrintMessage.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class PrintMessage implements StepBody { 7 | 8 | public String message; 9 | 10 | @Override 11 | public ExecutionResult run(StepExecutionContext context) { 12 | System.out.println(message); 13 | return ExecutionResult.next(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/sample02/src/main/java/net/jworkflow/sample02/steps/DisplayAnswer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample02.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class DisplayAnswer implements StepBody { 7 | 8 | public int answer; 9 | 10 | @Override 11 | public ExecutionResult run(StepExecutionContext context) { 12 | System.out.println("The answer is " + answer); 13 | return ExecutionResult.next(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | sudo: true 4 | 5 | services: 6 | - docker 7 | 8 | jdk: 9 | - oraclejdk8 10 | 11 | install: 12 | - cd jworkflow.kernel 13 | - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 14 | - cd .. 15 | - cd jworkflow.providers.mongodb 16 | - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 17 | - cd .. 18 | 19 | script: 20 | - cd jworkflow.kernel 21 | - mvn test -B 22 | - cd .. 23 | - cd jworkflow.providers.mongodb 24 | - mvn test -B 25 | - cd .. 26 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/steps/IncrementValue.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class IncrementValue implements StepBody { 7 | 8 | public int value; 9 | 10 | @Override 11 | public ExecutionResult run(StepExecutionContext context) { 12 | System.out.println("Incrementing..."); 13 | value++; 14 | return ExecutionResult.next(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/PointerStatus.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public enum PointerStatus { 4 | 5 | Pending(1), 6 | Running(2), 7 | Complete(3), 8 | Sleeping(4), 9 | WaitingForEvent(5), 10 | Failed(6), 11 | Compensated(7); 12 | 13 | private final int value; 14 | 15 | public int getValue() { 16 | return this.value; 17 | } 18 | 19 | private PointerStatus(int value) { 20 | this.value = value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/ExecutionResultProcessor.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.*; 4 | 5 | public interface ExecutionResultProcessor { 6 | void processExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result, WorkflowExecutorResult workflowResult); 7 | void handleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step); 8 | } 9 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/steps/AddNumbers.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class AddNumbers implements StepBody { 7 | 8 | public int value1; 9 | public int value2; 10 | public int result; 11 | 12 | @Override 13 | public ExecutionResult run(StepExecutionContext context) { 14 | result = value1 + value2; 15 | return ExecutionResult.next(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/sample02/src/main/java/net/jworkflow/sample02/steps/AddNumbers.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample02.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.*; 5 | 6 | public class AddNumbers implements StepBody { 7 | 8 | public int number1; 9 | public int number2; 10 | public int answer; 11 | 12 | @Override 13 | public ExecutionResult run(StepExecutionContext context) { 14 | answer = number1 + number2; 15 | return ExecutionResult.next(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/src/main/java/net/jworkflow/providers/aws/SQSProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.aws; 2 | 3 | import com.google.inject.Provider; 4 | import software.amazon.awssdk.regions.Region; 5 | 6 | public class SQSProvider implements Provider { 7 | 8 | private final Region region; 9 | 10 | public SQSProvider(Region region) { 11 | this.region = region; 12 | } 13 | 14 | @Override 15 | public SQSQueueService get() { 16 | return new SQSQueueService(region); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /docs/07-schedule.md: -------------------------------------------------------------------------------- 1 | ### Schedule 2 | 3 | Use `.schedule` to register a future set of steps to run asynchronously in the background within your workflow. 4 | 5 | 6 | ```java 7 | @Override 8 | public void build(WorkflowBuilder builder) { 9 | builder 10 | .startsWith(Hello.class) 11 | .schedule(data -> Duration.ofMinutes(10)) 12 | .Do(schedule -> schedule 13 | .startsWith(DoSomething.class) 14 | .then(DoSomethingElse.class) 15 | ) 16 | .then(Goodbye.class); 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /samples/sample03/src/main/java/net/jworkflow/sample03/steps/DisplayAnswer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample03.steps; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.ExecutionResult; 5 | import net.jworkflow.kernel.models.StepExecutionContext; 6 | 7 | public class DisplayAnswer implements StepBody { 8 | 9 | public Object answer; 10 | 11 | @Override 12 | public ExecutionResult run(StepExecutionContext context) { 13 | System.out.println("The answer is " + answer); 14 | return ExecutionResult.next(); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /jworkflow.providers.redis/src/main/java/net/jworkflow/providers/redis/RedisLockServiceProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.redis; 2 | 3 | import com.google.inject.Provider; 4 | import org.redisson.config.Config; 5 | 6 | public class RedisLockServiceProvider implements Provider { 7 | 8 | private final Config config; 9 | 10 | public RedisLockServiceProvider(Config config) { 11 | this.config = config; 12 | } 13 | 14 | @Override 15 | public RedisLockService get() { 16 | return new RedisLockService(config); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/services/test/MemoryPersistenceServiceTest.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services.test; 2 | import net.jworkflow.kernel.services.MemoryPersistenceService; 3 | import net.jworkflow.kernel.services.abstractions.PersistenceServiceTest; 4 | import org.junit.Test; 5 | import net.jworkflow.kernel.interfaces.PersistenceService; 6 | 7 | public class MemoryPersistenceServiceTest extends PersistenceServiceTest { 8 | 9 | @Override 10 | public PersistenceService createService() { 11 | return new MemoryPersistenceService(); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /jworkflow.providers.redis/src/main/java/net/jworkflow/providers/redis/RedisQueueServiceProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.redis; 2 | 3 | import com.google.inject.Provider; 4 | import org.redisson.config.Config; 5 | 6 | public class RedisQueueServiceProvider implements Provider { 7 | 8 | private final Config config; 9 | 10 | public RedisQueueServiceProvider(Config config) { 11 | this.config = config; 12 | } 13 | 14 | @Override 15 | public RedisQueueService get() { 16 | return new RedisQueueService(config); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/ConsumerStep.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.util.function.Consumer; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | 8 | public class ConsumerStep implements StepBody { 9 | 10 | public Consumer body; 11 | 12 | @Override 13 | public ExecutionResult run(StepExecutionContext context) throws Exception { 14 | body.accept(context); 15 | return ExecutionResult.next(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /jworkflow.kernel/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | workflow 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/sample01/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample01.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/sample02/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample02.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/sample03/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample03.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/sample04/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample04.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/sample05/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample05.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/sample08/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample08.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /scratchpad/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath com.jworkflow.scratchpad.Main 14 | java 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/src/main/java/net/jworkflow/providers/aws/DynamoDBLockProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.aws; 2 | 3 | import com.google.inject.Provider; 4 | import software.amazon.awssdk.regions.Region; 5 | 6 | public class DynamoDBLockProvider implements Provider { 7 | 8 | private final Region region; 9 | private final String tableName; 10 | 11 | public DynamoDBLockProvider(Region region, String tableName) { 12 | this.region = region; 13 | this.tableName = tableName; 14 | } 15 | 16 | @Override 17 | public DynamoDBLockService get() { 18 | return new DynamoDBLockService(region, tableName); 19 | } 20 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/definitionstorage/models/DefinitionSource.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.definitionstorage.models; 2 | 3 | import java.time.Duration; 4 | import java.util.List; 5 | import java.util.ArrayList; 6 | import net.jworkflow.kernel.models.ErrorBehavior; 7 | 8 | public class DefinitionSource { 9 | 10 | public int schemaVersion; 11 | 12 | public String id; 13 | 14 | public int version; 15 | 16 | public String description; 17 | 18 | public String dataType; 19 | 20 | public ErrorBehavior defaultErrorBehavior; 21 | 22 | public Duration defaultErrorRetryInterval; 23 | 24 | public List steps = new ArrayList<>(); 25 | } 26 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/Delay.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.time.Duration; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | 8 | public class Delay implements StepBody { 9 | 10 | public Duration duration; 11 | 12 | @Override 13 | public ExecutionResult run(StepExecutionContext context) { 14 | 15 | if (context.getPersistenceData() != null) { 16 | return ExecutionResult.next(); 17 | } 18 | 19 | return ExecutionResult.sleep(duration, true); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/ExecutionPointerFactory.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.*; 4 | 5 | public interface ExecutionPointerFactory { 6 | ExecutionPointer buildGenesisPointer(WorkflowDefinition def); 7 | ExecutionPointer buildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId); 8 | ExecutionPointer buildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, StepOutcome outcomeTarget); 9 | ExecutionPointer buildChildPointer(WorkflowDefinition def, ExecutionPointer pointer, int childDefinitionId, Object branch); 10 | } 11 | -------------------------------------------------------------------------------- /scratchpad/src/main/java/com/jworkflow/scratchpad/TestData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.jworkflow.scratchpad; 7 | 8 | import java.util.Properties; 9 | 10 | /** 11 | * 12 | * @author Daniel.Gerlag 13 | */ 14 | public class TestData { 15 | private int pid; 16 | 17 | public int id; 18 | public String name; 19 | 20 | 21 | public int getPid() { 22 | return pid; 23 | } 24 | 25 | 26 | public void setPid(int pid) { 27 | this.pid = pid; 28 | } 29 | 30 | 31 | 32 | } -------------------------------------------------------------------------------- /samples/sample01/README.md: -------------------------------------------------------------------------------- 1 | # Hello World Sample 2 | 3 | Illustrates the basic usage of the fluent API for defining workflows. 4 | 5 | ```java 6 | public class HelloWorkflow implements Workflow { 7 | 8 | @Override 9 | public void build(WorkflowBuilder builder) { 10 | builder 11 | .startsWith(Hello.class) 12 | .then(Goodbye.class); 13 | } 14 | ... 15 | } 16 | ``` 17 | 18 | and how to define steps 19 | 20 | ```java 21 | public class Hello implements StepBody { 22 | 23 | @Override 24 | public ExecutionResult run(StepExecutionContext context) { 25 | System.out.println("Hello world"); 26 | return ExecutionResult.next(); 27 | } 28 | } 29 | ``` 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/sample05/README.md: -------------------------------------------------------------------------------- 1 | # While sample 2 | 3 | Illustrates how to implement a while loop within your workflow. 4 | 5 | 6 | ```java 7 | public class WhileWorkflow implements Workflow { 8 | 9 | @Override 10 | public void build(WorkflowBuilder builder) { 11 | builder 12 | .startsWith(Hello.class) 13 | .While(data -> data.value1 < 3) 14 | .Do(each -> each 15 | .startsWith(IncrementValue.class) 16 | .input((step, data) -> step.value = data.value1) 17 | .output((step, data) -> data.value1 = step.value) 18 | ) 19 | .then(Goodbye.class); 20 | } 21 | ... 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/StepOutcome.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public class StepOutcome { 4 | private Object value; 5 | private Integer nextStep; 6 | private String tag; 7 | 8 | public Object getValue() { 9 | return value; 10 | } 11 | 12 | public void setValue(Object value) { 13 | this.value = value; 14 | } 15 | 16 | public Integer getNextStep() { 17 | return nextStep; 18 | } 19 | 20 | public void setNextStep(Integer nextStep) { 21 | this.nextStep = nextStep; 22 | } 23 | 24 | public String getTag() { 25 | return tag; 26 | } 27 | 28 | public void setTag(String tag) { 29 | this.tag = tag; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/sample07/README.md: -------------------------------------------------------------------------------- 1 | # Saga Transaction sample 2 | 3 | Illustrates how to implement an saga transaction with compensating steps, should an unhandled exception occur within the saga. 4 | 5 | 6 | ```java 7 | @Override 8 | public void build(WorkflowBuilder builder) { 9 | builder 10 | .startsWithAction(context -> System.out.println("begin")) 11 | .saga(saga -> saga 12 | .startsWith(Task1.class) 13 | .compensateWith(UndoTask1.class) 14 | .then(Task2.class) 15 | .compensateWith(UndoTask2.class) 16 | .then(Task3.class) 17 | .compensateWith(UndoTask3.class) 18 | ) 19 | .thenAction(context -> System.out.println("end")); 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/errorhandlers/SuspendHandler.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.errorhandlers; 2 | 3 | import com.google.inject.Singleton; 4 | import java.util.Queue; 5 | import net.jworkflow.kernel.interfaces.*; 6 | import net.jworkflow.kernel.models.*; 7 | 8 | @Singleton 9 | public class SuspendHandler implements StepErrorHandler { 10 | 11 | @Override 12 | public ErrorBehavior getErrorBehavior() { 13 | return ErrorBehavior.SUSPEND; 14 | } 15 | 16 | @Override 17 | public void handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Queue bubleupQueue) { 18 | workflow.setStatus(WorkflowStatus.SUSPENDED); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/errorhandlers/TerminateHandler.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.errorhandlers; 2 | 3 | import com.google.inject.Singleton; 4 | import java.util.Queue; 5 | import net.jworkflow.kernel.interfaces.*; 6 | import net.jworkflow.kernel.models.*; 7 | 8 | @Singleton 9 | public class TerminateHandler implements StepErrorHandler { 10 | 11 | @Override 12 | public ErrorBehavior getErrorBehavior() { 13 | return ErrorBehavior.TERMINATE; 14 | } 15 | 16 | @Override 17 | public void handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Queue bubleupQueue) { 18 | workflow.setStatus(WorkflowStatus.TERMINATED); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /jworkflow.providers.rabbitmq/src/main/java/net/jworkflow/providers/rabbitmq/RabbitMQProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.rabbitmq; 2 | 3 | import com.google.inject.Provider; 4 | import com.rabbitmq.client.ConnectionFactory; 5 | 6 | public class RabbitMQProvider implements Provider { 7 | 8 | private final ConnectionFactory connectionFactory; 9 | 10 | public RabbitMQProvider(ConnectionFactory connectionFactory) { 11 | this.connectionFactory = connectionFactory; 12 | } 13 | 14 | @Override 15 | public RabbitMQQueueService get() { 16 | try { 17 | return new RabbitMQQueueService(connectionFactory); 18 | } catch (Exception ex) { 19 | throw new RuntimeException(ex); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /samples/sample01/src/main/java/net/jworkflow/sample01/HelloWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample01; 2 | 3 | import net.jworkflow.sample01.steps.Goodbye; 4 | import net.jworkflow.sample01.steps.Hello; 5 | import net.jworkflow.kernel.interfaces.*; 6 | 7 | public class HelloWorkflow implements Workflow { 8 | 9 | @Override 10 | public String getId() { 11 | return "hello"; 12 | } 13 | 14 | @Override 15 | public int getVersion() { 16 | return 1; 17 | } 18 | 19 | @Override 20 | public Class getDataType() { 21 | return Object.class; 22 | } 23 | 24 | @Override 25 | public void build(WorkflowBuilder builder) { 26 | builder 27 | .startsWith(Hello.class) 28 | .then(Goodbye.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/SagaContainer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import net.jworkflow.kernel.models.ExecutionPointer; 4 | import net.jworkflow.kernel.models.WorkflowStep; 5 | 6 | public class SagaContainer extends WorkflowStep { 7 | 8 | public SagaContainer(Class bodyType) { 9 | super(bodyType); 10 | } 11 | 12 | @Override 13 | public boolean getResumeChildrenAfterCompensation() { 14 | return false; 15 | } 16 | 17 | @Override 18 | public boolean getRevertChildrenAfterCompensation() { 19 | return true; 20 | } 21 | 22 | @Override 23 | public void primeForRetry(ExecutionPointer pointer) { 24 | super.primeForRetry(pointer); 25 | pointer.persistenceData = null; 26 | } 27 | } -------------------------------------------------------------------------------- /jworkflow.providers.aws/src/main/java/net/jworkflow/providers/aws/DynamoDBPersistenceProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.aws; 2 | 3 | import com.google.inject.Provider; 4 | import software.amazon.awssdk.regions.Region; 5 | 6 | public class DynamoDBPersistenceProvider implements Provider { 7 | 8 | private final Region region; 9 | private final String tablePrefix; 10 | 11 | public DynamoDBPersistenceProvider(Region region, String tablePrefix) { 12 | this.region = region; 13 | this.tablePrefix = tablePrefix; 14 | } 15 | 16 | @Override 17 | public DynamoDBPersistenceService get() { 18 | DynamoDBProvisioner provisioner = new DefaultDynamoDBProvisioner(region, tablePrefix); 19 | return new DynamoDBPersistenceService(region, provisioner, tablePrefix); 20 | } 21 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/Recur.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.time.Duration; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | 8 | public class Recur implements StepBody { 9 | 10 | public Duration interval; 11 | public boolean stopCondition; 12 | 13 | @Override 14 | public ExecutionResult run(StepExecutionContext context) { 15 | 16 | if (stopCondition) { 17 | return ExecutionResult.next(); 18 | } 19 | ExecutionResult result = new ExecutionResult(); 20 | result.setProceed(false); 21 | result.setSleepFor(interval); 22 | result.setBranches(new Object[1]); 23 | 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jworkflow.providers.mongodb/src/main/java/net/jworkflow/providers/mongodb/MongoPersistenceServiceProvider.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.mongodb; 2 | 3 | import com.google.inject.Provider; 4 | import java.net.UnknownHostException; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | public class MongoPersistenceServiceProvider implements Provider{ 9 | 10 | String uri; 11 | 12 | public MongoPersistenceServiceProvider(String uri) { 13 | this.uri = uri; 14 | } 15 | 16 | @Override 17 | public MongoPersistenceService get() { 18 | try { 19 | return new MongoPersistenceService(uri); 20 | } catch (UnknownHostException ex) { 21 | Logger.getLogger(MongoPersistenceServiceProvider.class.getName()).log(Level.SEVERE, null, ex); 22 | } 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/sample08/src/main/resources/workflow3.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-workflow", 3 | "version": 1, 4 | "dataType": "net.jworkflow.sample08.MyData", 5 | "steps": [ 6 | { 7 | "id": "step1", 8 | "stepType": "net.jworkflow.sample08.steps.Hello", 9 | "nextStepId": "step2" 10 | }, 11 | { 12 | "id": "step2", 13 | "stepType": "net.jworkflow.primitives.Foreach", 14 | "nextStepId": "step3", 15 | "inputs": { 16 | "collection": "data.collection1" 17 | }, 18 | "thenDo": [[ 19 | { 20 | "id": "step2.1", 21 | "stepType": "net.jworkflow.sample08.steps.PrintMessage", 22 | "inputs": { 23 | "message": "'doing 2.1'" 24 | } 25 | } 26 | ]] 27 | }, 28 | { 29 | "id": "step3", 30 | "stepType": "net.jworkflow.sample08.steps.Goodbye" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/WaitFor.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.util.Date; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | 8 | public class WaitFor implements StepBody { 9 | 10 | public String eventKey; 11 | public String eventName; 12 | public Date effectiveDate; 13 | public Object eventData; 14 | 15 | @Override 16 | public ExecutionResult run(StepExecutionContext context) { 17 | 18 | if (!context.getExecutionPointer().eventPublished) { 19 | return ExecutionResult.waitForEvent(eventName, eventKey, effectiveDate); 20 | } 21 | 22 | eventData = context.getExecutionPointer().eventData; 23 | return ExecutionResult.next(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | samples 6 | 1.0-SNAPSHOT 7 | pom 8 | 9 | sample01 10 | sample02 11 | sample03 12 | sample04 13 | sample05 14 | sample06 15 | sample07 16 | sample08 17 | 18 | 19 | UTF-8 20 | 21 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/StepOutcomeBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.function.Consumer; 4 | import java.util.function.Function; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | import net.jworkflow.kernel.models.WorkflowStepInline; 8 | 9 | public interface StepOutcomeBuilder { 10 | 11 | StepBuilder then(Class stepClass); 12 | 13 | StepBuilder then(Class stepClass, Consumer> stepSetup); 14 | 15 | StepBuilder then(Class stepClass, StepBuilder newStep); 16 | 17 | StepBuilder then(Function body); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /samples/sample08/src/main/resources/workflow2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-workflow", 3 | "version": 1, 4 | "dataType": "net.jworkflow.sample08.MyData", 5 | "steps": [ 6 | { 7 | "id": "step1", 8 | "stepType": "net.jworkflow.sample08.steps.Hello", 9 | "nextStepId": "step2" 10 | }, 11 | { 12 | "id": "step2", 13 | "stepType": "net.jworkflow.sample08.steps.AddNumbers", 14 | "nextStepId": "step3", 15 | "inputs": { 16 | "value1": "data.value1", 17 | "value2": "data.value2" 18 | }, 19 | "outputs": { 20 | "value3": "step.result" 21 | } 22 | }, 23 | { 24 | "id": "step3", 25 | "stepType": "net.jworkflow.sample08.steps.PrintMessage", 26 | "nextStepId": "step4", 27 | "inputs": { 28 | "message": "'The answer is ' + data.value3" 29 | } 30 | }, 31 | { 32 | "id": "step4", 33 | "stepType": "net.jworkflow.sample08.steps.Goodbye" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/definitionstorage/models/StepSource.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.definitionstorage.models; 2 | 3 | import java.time.Duration; 4 | import java.util.List; 5 | import java.util.ArrayList; 6 | import java.util.Dictionary; 7 | import java.util.Properties; 8 | import net.jworkflow.kernel.models.ErrorBehavior; 9 | 10 | public class StepSource { 11 | 12 | public String stepType; 13 | 14 | public String id; 15 | 16 | public String name; 17 | 18 | public String cancelCondition; 19 | 20 | public ErrorBehavior errorBehavior; 21 | 22 | public Duration retryInterval; 23 | 24 | public List> thenDo = new ArrayList<>(); 25 | 26 | public List compensateWith = new ArrayList<>(); 27 | 28 | public boolean saga = false; 29 | 30 | public String nextStepId; 31 | 32 | public Properties inputs = new Properties(); 33 | 34 | public Properties outputs = new Properties(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/SingleNodeLockService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | import net.jworkflow.kernel.interfaces.LockService; 3 | import com.google.inject.Singleton; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | @Singleton 8 | public class SingleNodeLockService implements LockService{ 9 | 10 | private final List locks; 11 | 12 | public SingleNodeLockService() { 13 | locks = new ArrayList<>(); 14 | } 15 | 16 | @Override 17 | public synchronized boolean acquireLock(String id) { 18 | if (locks.contains(id)) 19 | return false; 20 | 21 | locks.add(id); 22 | return true; 23 | } 24 | 25 | @Override 26 | public synchronized void releaseLock(String id) { 27 | locks.remove(id); 28 | } 29 | 30 | @Override 31 | public void start() { 32 | 33 | } 34 | 35 | @Override 36 | public void stop() { 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/WorkflowBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.function.Consumer; 4 | import net.jworkflow.kernel.models.StepExecutionContext; 5 | import net.jworkflow.kernel.models.WorkflowDefinition; 6 | import net.jworkflow.kernel.models.WorkflowStep; 7 | import net.jworkflow.kernel.models.WorkflowStepInline; 8 | import net.jworkflow.primitives.ConsumerStep; 9 | 10 | public interface WorkflowBuilder { 11 | void addStep(WorkflowStep step); 12 | int getLastStep(); 13 | StepBuilder startsWith(Class stepClass); 14 | WorkflowDefinition build(String id, int version); 15 | StepBuilder startsWith(Class stepClass, Consumer> stepSetup); 16 | StepBuilder startsWith(StepExecutionConsumer body); 17 | StepBuilder startsWithAction(Consumer body); 18 | } 19 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/WorkflowHost.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import java.util.Date; 4 | 5 | public interface WorkflowHost { 6 | String startWorkflow(String workflowId, int version, Object data) throws Exception; 7 | void start(); 8 | void stop(); 9 | void registerWorkflow(Class workflow) throws Exception; 10 | void registerWorkflow(Workflow workflow) throws Exception; 11 | void registerWorkflowFromJson(String json) throws Exception; 12 | void publishEvent(String eventName, String eventKey, Object eventData, Date effectiveDateUtc) throws Exception; 13 | 14 | /** 15 | * Suspend the given workflow, so it will not be executed until resumed 16 | * @param workflowId 17 | * @return 18 | */ 19 | boolean suspendWorkflow(String workflowId); 20 | 21 | /** 22 | * Resume a previously suspended workflow, so execution can continue 23 | * @param workflowId 24 | * @return 25 | */ 26 | boolean resumeWorkflow(String workflowId); 27 | } 28 | -------------------------------------------------------------------------------- /samples/sample03/src/main/java/net/jworkflow/sample03/EventsWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample03; 2 | 3 | import net.jworkflow.sample03.steps.Hello; 4 | import net.jworkflow.sample03.steps.DisplayAnswer; 5 | import net.jworkflow.kernel.interfaces.Workflow; 6 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 7 | 8 | public class EventsWorkflow implements Workflow { 9 | 10 | @Override 11 | public String getId() { 12 | return "events-workflow"; 13 | } 14 | 15 | @Override 16 | public Class getDataType() { 17 | return MyData.class; 18 | } 19 | 20 | @Override 21 | public int getVersion() { 22 | return 1; 23 | } 24 | 25 | @Override 26 | public void build(WorkflowBuilder builder) { 27 | builder 28 | .startsWith(Hello.class) 29 | .waitFor("myEvent", x -> "1") 30 | .output((step, data) -> data.value1 = step.eventData) 31 | .then(DisplayAnswer.class) 32 | .input((step, data) -> step.answer = data.value1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ExecutionPointer.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Duration; 5 | import java.util.ArrayList; 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.Stack; 9 | 10 | public class ExecutionPointer implements Serializable { 11 | public String id; 12 | public int stepId; 13 | public boolean active; 14 | public Date sleepUntil; 15 | public Object persistenceData; 16 | public Date startTimeUtc; 17 | public Date endTimeUtc; 18 | public String eventName; 19 | public String eventKey; 20 | public boolean eventPublished; 21 | public Object eventData; 22 | public int retryCounter; 23 | public String predecessorId; 24 | public Object contextItem; 25 | public List children; 26 | public Stack callStack; 27 | public PointerStatus status; 28 | 29 | public ExecutionPointer() { 30 | this.children = new ArrayList<>(); 31 | this.callStack = new Stack<>(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /jworkflow.providers.mongodb/README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Persistence provider for JWorkflow 2 | 3 | Provides support to persist workflows running on [JWorkflow](../README.md) to a MongoDB database. 4 | 5 | ## Installing 6 | 7 | ### Using Maven 8 | 9 | ```xml 10 | 11 | 12 | net.jworkflow 13 | jworkflow.providers.mongodb 14 | 0.5-SNAPSHOT 15 | 16 | 17 | ``` 18 | 19 | ### Using Gradle 20 | 21 | ```Gradle 22 | dependencies { 23 | compile 'net.jworkflow:jworkflow.providers.mongodb:0.5-SNAPSHOT' 24 | } 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | Use the MongoPersistenceService.configure static method when bootstraping your application. 31 | 32 | ```java 33 | import net.jworkflow.providers.mongodb.MongoPersistenceService; 34 | 35 | ... 36 | 37 | WorkflowModule module = new WorkflowModule(); 38 | module.usePersistence(MongoPersistenceService.configure("mongodb://localhost:27017/jworkflow")); 39 | module.build(); 40 | WorkflowHost host = module.getHost(); 41 | 42 | ``` 43 | -------------------------------------------------------------------------------- /samples/sample01/src/main/java/net/jworkflow/sample01/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample01; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowHost; 4 | import net.jworkflow.WorkflowModule; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class Main { 10 | public static void main(String[] args) throws Exception { 11 | Logger rootLogger = Logger.getLogger(""); 12 | rootLogger.setLevel(Level.SEVERE); 13 | 14 | WorkflowModule module = new WorkflowModule(); 15 | module.build(); 16 | WorkflowHost host = module.getHost(); 17 | 18 | host.registerWorkflow(HelloWorkflow.class); 19 | 20 | host.start(); 21 | 22 | String id = host.startWorkflow("hello", 1, null); 23 | System.out.println("started workflow " + id); 24 | 25 | 26 | Scanner scanner = new Scanner(System.in); 27 | scanner.nextLine(); 28 | 29 | System.out.println("shutting down..."); 30 | host.stop(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/sample02/src/main/java/net/jworkflow/sample02/DataWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample02; 2 | 3 | import net.jworkflow.sample02.steps.DisplayAnswer; 4 | import net.jworkflow.sample02.steps.AddNumbers; 5 | import net.jworkflow.kernel.interfaces.*; 6 | 7 | public class DataWorkflow implements Workflow { 8 | 9 | @Override 10 | public String getId() { 11 | return "data-workflow"; 12 | } 13 | 14 | @Override 15 | public Class getDataType() { 16 | return MyData.class; 17 | } 18 | 19 | @Override 20 | public int getVersion() { 21 | return 1; 22 | } 23 | 24 | @Override 25 | public void build(WorkflowBuilder builder) { 26 | builder 27 | .startsWith(AddNumbers.class) 28 | .input((step, data) -> step.number1 = data.value1) 29 | .input((step, data) -> step.number2 = data.value2) 30 | .output((step, data) -> data.value3 = step.answer) 31 | .then(DisplayAnswer.class) 32 | .input((step, data) -> step.answer = data.value3); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Daniel Gerlag 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowHost; 4 | import net.jworkflow.WorkflowModule; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class Main { 10 | public static void main(String[] args) throws Exception { 11 | Logger rootLogger = Logger.getLogger(""); 12 | rootLogger.setLevel(Level.SEVERE); 13 | 14 | WorkflowModule module = new WorkflowModule(); 15 | module.build(); 16 | WorkflowHost host = module.getHost(); 17 | 18 | host.registerWorkflow(CompensatingWorkflow.class); 19 | 20 | host.start(); 21 | 22 | String id = host.startWorkflow("saga-sample", 1, null); 23 | System.out.println("started workflow " + id); 24 | 25 | 26 | Scanner scanner = new Scanner(System.in); 27 | scanner.nextLine(); 28 | 29 | System.out.println("shutting down..."); 30 | host.stop(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/interfaces/PersistenceService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.interfaces; 2 | 3 | import net.jworkflow.kernel.models.EventSubscription; 4 | import net.jworkflow.kernel.models.Event; 5 | import net.jworkflow.kernel.models.WorkflowInstance; 6 | import java.util.Date; 7 | 8 | public interface PersistenceService { 9 | String createNewWorkflow(WorkflowInstance workflow); 10 | void persistWorkflow(WorkflowInstance workflow); 11 | Iterable getRunnableInstances(); 12 | WorkflowInstance getWorkflowInstance(String id); 13 | String createEventSubscription(EventSubscription subscription); 14 | Iterable getSubcriptions(String eventName, String eventKey, Date asOf); 15 | void terminateSubscription(String eventSubscriptionId); 16 | String createEvent(Event newEvent); 17 | Event getEvent(String id); 18 | Iterable getRunnableEvents(); 19 | Iterable getEvents(String eventName, String eventKey, Date asOf); 20 | void markEventProcessed(String id); 21 | void markEventUnprocessed(String id); 22 | 23 | void provisionStore(); 24 | } 25 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/ForeachWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04; 2 | 3 | import net.jworkflow.sample04.steps.*; 4 | import net.jworkflow.kernel.interfaces.*; 5 | 6 | public class ForeachWorkflow implements Workflow { 7 | 8 | @Override 9 | public String getId() { 10 | return "foreach-sample"; 11 | } 12 | 13 | @Override 14 | public Class getDataType() { 15 | return MyData.class; 16 | } 17 | 18 | @Override 19 | public int getVersion() { 20 | return 1; 21 | } 22 | 23 | @Override 24 | public void build(WorkflowBuilder builder) { 25 | 26 | builder 27 | .startsWith(Hello.class) 28 | .foreach(data -> data.value1) 29 | .Do(each -> each 30 | .startsWith(DoSomething.class)) 31 | .then(Hello.class) 32 | .foreach(data -> new String[] { "item 1", "item 2", "item 3" }) 33 | .Do(each -> each 34 | .startsWith(DoSomething.class)) 35 | .then(Goodbye.class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /samples/sample08/src/main/resources/workflow4.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-workflow", 3 | "version": 1, 4 | "dataType": "net.jworkflow.sample08.MyData", 5 | "steps": [ 6 | { 7 | "id": "step1", 8 | "stepType": "net.jworkflow.sample08.steps.Hello", 9 | "nextStepId": "step2" 10 | }, 11 | { 12 | "id": "step2", 13 | "stepType": "net.jworkflow.primitives.If", 14 | "nextStepId": "step3", 15 | "inputs": { 16 | "condition": "data.value1 == 2" 17 | }, 18 | "thenDo": [[ 19 | { 20 | "id": "step2.1", 21 | "stepType": "net.jworkflow.sample08.steps.PrintMessage", 22 | "nextStepId": "step2.2", 23 | "inputs": { 24 | "message": "'doing 2.1'" 25 | } 26 | }, 27 | { 28 | "id": "step2.2", 29 | "stepType": "net.jworkflow.sample08.steps.PrintMessage", 30 | "inputs": { 31 | "message": "'doing 2.2'" 32 | } 33 | } 34 | ]] 35 | }, 36 | { 37 | "id": "step3", 38 | "stepType": "net.jworkflow.sample08.steps.Goodbye" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/errorhandlers/RetryHandler.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.errorhandlers; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import java.time.Clock; 6 | import java.time.Instant; 7 | import java.util.Date; 8 | import java.util.Queue; 9 | import net.jworkflow.kernel.interfaces.*; 10 | import net.jworkflow.kernel.models.*; 11 | 12 | @Singleton 13 | public class RetryHandler implements StepErrorHandler { 14 | 15 | private final Clock clock; 16 | 17 | @Inject 18 | public RetryHandler(Clock clock) { 19 | this.clock = clock; 20 | } 21 | 22 | @Override 23 | public ErrorBehavior getErrorBehavior() { 24 | return ErrorBehavior.RETRY; 25 | } 26 | 27 | @Override 28 | public void handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Queue bubleupQueue) { 29 | pointer.retryCounter++; 30 | pointer.sleepUntil = Date.from(Instant.now(clock).plus(step.getRetryInterval())); 31 | step.primeForRetry(pointer); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /samples/sample04/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample04 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | net.jworkflow 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | net.jworkflow 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | -------------------------------------------------------------------------------- /samples/sample05/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample05 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | net.jworkflow 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | net.jworkflow 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | -------------------------------------------------------------------------------- /samples/sample06/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample06 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | net.jworkflow 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | net.jworkflow 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | -------------------------------------------------------------------------------- /samples/sample07/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample07 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | net.jworkflow 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | net.jworkflow 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | -------------------------------------------------------------------------------- /samples/sample01/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample01 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | ${project.groupId} 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | ${project.groupId} 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowHost; 4 | import net.jworkflow.WorkflowModule; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class Main { 10 | public static void main(String[] args) throws Exception { 11 | Logger rootLogger = Logger.getLogger(""); 12 | rootLogger.setLevel(Level.SEVERE); 13 | 14 | WorkflowModule module = new WorkflowModule(); 15 | module.build(); 16 | WorkflowHost host = module.getHost(); 17 | 18 | host.registerWorkflow(IfWorkflow.class); 19 | 20 | host.start(); 21 | 22 | MyData data = new MyData(); 23 | data.value1 = 3; 24 | 25 | String id = host.startWorkflow("if-sample", 1, data); 26 | System.out.println("started workflow " + id); 27 | 28 | 29 | Scanner scanner = new Scanner(System.in); 30 | scanner.nextLine(); 31 | 32 | System.out.println("shutting down..."); 33 | host.stop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/sample08/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample08 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | net.jworkflow 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | net.jworkflow 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 25 | 26 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowHost; 4 | import net.jworkflow.WorkflowModule; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class Main { 10 | public static void main(String[] args) throws Exception { 11 | Logger rootLogger = Logger.getLogger(""); 12 | rootLogger.setLevel(Level.SEVERE); 13 | 14 | WorkflowModule module = new WorkflowModule(); 15 | module.build(); 16 | WorkflowHost host = module.getHost(); 17 | 18 | host.registerWorkflow(WhileWorkflow.class); 19 | 20 | host.start(); 21 | 22 | MyData data = new MyData(); 23 | data.value1 = 0; 24 | 25 | String id = host.startWorkflow("while-sample", 1, data); 26 | System.out.println("started workflow " + id); 27 | 28 | 29 | Scanner scanner = new Scanner(System.in); 30 | scanner.nextLine(); 31 | 32 | System.out.println("shutting down..."); 33 | host.stop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/sample05/src/main/java/net/jworkflow/sample05/WhileWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample05; 2 | 3 | import net.jworkflow.sample05.steps.Goodbye; 4 | import net.jworkflow.sample05.steps.Hello; 5 | import net.jworkflow.kernel.interfaces.*; 6 | import net.jworkflow.sample05.steps.IncrementValue; 7 | 8 | public class WhileWorkflow implements Workflow { 9 | 10 | @Override 11 | public String getId() { 12 | return "while-sample"; 13 | } 14 | 15 | @Override 16 | public Class getDataType() { 17 | return MyData.class; 18 | } 19 | 20 | @Override 21 | public int getVersion() { 22 | return 1; 23 | } 24 | 25 | @Override 26 | public void build(WorkflowBuilder builder) { 27 | builder 28 | .startsWith(Hello.class) 29 | .While(data -> data.value1 < 3) 30 | .Do(each -> each 31 | .startsWith(IncrementValue.class) 32 | .input((step, data) -> step.value = data.value1) 33 | .output((step, data) -> data.value1 = step.value) 34 | ) 35 | .then(Goodbye.class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /samples/sample06/README.md: -------------------------------------------------------------------------------- 1 | # If sample 2 | 3 | Illustrates how to implement an If decision within your workflow. 4 | 5 | 6 | ```java 7 | public class IfWorkflow implements Workflow { 8 | 9 | @Override 10 | public void build(WorkflowBuilder builder) { 11 | builder 12 | .startsWith(Hello.class) 13 | .If(data -> data.value1 > 2) 14 | .Do(then -> then 15 | .startsWith(PrintMessage.class) 16 | .input((step, data) -> step.message = "Value is greater than 2") 17 | .then(PrintMessage.class) 18 | .input((step, data) -> step.message = "Doing something...") 19 | ) 20 | .If(data -> data.value1 == 5) 21 | .Do(then -> then 22 | .startsWith(PrintMessage.class) 23 | .input((step, data) -> step.message = "Value is 5") 24 | .then(PrintMessage.class) 25 | .input((step, data) -> step.message = "Doing something...") 26 | ) 27 | .then(Goodbye.class); 28 | } 29 | ... 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/Sequence.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.util.Arrays; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ControlStepData; 6 | import net.jworkflow.kernel.models.ExecutionResult; 7 | import net.jworkflow.kernel.models.StepExecutionContext; 8 | 9 | public class Sequence implements StepBody { 10 | 11 | @Override 12 | public ExecutionResult run(StepExecutionContext context) { 13 | 14 | if (context.getPersistenceData() == null) { 15 | return ExecutionResult.branch(new Object[1], new ControlStepData(true)); 16 | } 17 | 18 | if (context.getPersistenceData() instanceof ControlStepData) { 19 | 20 | ControlStepData persistenceData = (ControlStepData)context.getPersistenceData(); 21 | 22 | if (persistenceData.childrenActive) { 23 | if (context.getWorkflow().isBranchComplete(context.getExecutionPointer().id)) 24 | return ExecutionResult.next(); 25 | } 26 | } 27 | 28 | return ExecutionResult.persist(context.getPersistenceData()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jworkflow.providers.rabbitmq/README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ provider for JWorkflow 2 | 3 | Provides support to run multi-node clusters of [JWorkflow](../README.md), by providing a distributed shared work queue. 4 | 5 | ## Installing 6 | 7 | ### Using Maven 8 | 9 | ```xml 10 | 11 | 12 | net.jworkflow 13 | jworkflow.providers.rabbitmq 14 | 0.5-SNAPSHOT 15 | 16 | 17 | ``` 18 | 19 | ### Using Gradle 20 | 21 | ```Gradle 22 | dependencies { 23 | compile 'net.jworkflow:jworkflow.providers.rabbitmq:0.5-SNAPSHOT' 24 | } 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | ```java 31 | import com.rabbitmq.client.ConnectionFactory; 32 | import net.jworkflow.providers.rabbitmq.RabbitMQProvider; 33 | ... 34 | ``` 35 | 36 | ```java 37 | WorkflowModule module = new WorkflowModule(); 38 | 39 | ConnectionFactory cf = new ConnectionFactory(); 40 | cf.setUsername("guest"); 41 | cf.setPassword("guest"); 42 | cf.setVirtualHost("/"); 43 | cf.setHost("localhost"); 44 | cf.setPort(5672); 45 | 46 | module.useQueue(new RabbitMQProvider(cf)); 47 | 48 | module.build(); 49 | WorkflowHost host = module.getHost(); 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/Foreach.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.models.ControlStepData; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | 8 | public class Foreach implements StepBody { 9 | 10 | public Object[] collection; 11 | 12 | @Override 13 | public ExecutionResult run(StepExecutionContext context) { 14 | 15 | if (context.getPersistenceData() == null) { 16 | return ExecutionResult.branch(collection, new ControlStepData(true)); 17 | } 18 | 19 | if (context.getPersistenceData() instanceof ControlStepData) { 20 | 21 | ControlStepData persistenceData = (ControlStepData)context.getPersistenceData(); 22 | 23 | if (persistenceData.childrenActive) { 24 | if (context.getWorkflow().isBranchComplete(context.getExecutionPointer().id)) 25 | return ExecutionResult.next(); 26 | } 27 | } 28 | 29 | return ExecutionResult.persist(context.getPersistenceData()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/sample07/src/main/java/net/jworkflow/sample07/CompensatingWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample07; 2 | 3 | import java.time.Duration; 4 | import net.jworkflow.sample07.steps.*; 5 | import net.jworkflow.kernel.interfaces.*; 6 | import net.jworkflow.kernel.models.ErrorBehavior; 7 | 8 | public class CompensatingWorkflow implements Workflow { 9 | 10 | @Override 11 | public String getId() { 12 | return "saga-sample"; 13 | } 14 | 15 | @Override 16 | public Class getDataType() { 17 | return Object.class; 18 | } 19 | 20 | @Override 21 | public int getVersion() { 22 | return 1; 23 | } 24 | 25 | @Override 26 | public void build(WorkflowBuilder builder) { 27 | builder 28 | .startsWithAction(context -> System.out.println("begin")) 29 | .saga(saga -> saga 30 | .startsWith(Task1.class) 31 | .compensateWith(UndoTask1.class) 32 | .then(Task2.class) 33 | .compensateWith(UndoTask2.class) 34 | .then(Task3.class) 35 | .compensateWith(UndoTask3.class) 36 | ) 37 | .thenAction(context -> System.out.println("end")); 38 | } 39 | } -------------------------------------------------------------------------------- /samples/sample03/README.md: -------------------------------------------------------------------------------- 1 | # Events sample 2 | 3 | Illustrates how to signal a workflow to wait for an external event, and how to invoke that event. 4 | 5 | We use the .waitFor() method on the workflow builder to signal the current execution path to wait until an event with a particular name (myEvent) and key (1) is published to the workflow host. 6 | 7 | ```java 8 | public class EventsWorkflow implements Workflow { 9 | 10 | @Override 11 | public void build(WorkflowBuilder builder) { 12 | builder 13 | .startsWith(Hello.class) 14 | .waitFor("myEvent", x -> "1") 15 | .output((step, data) -> data.value1 = step.eventData) 16 | .then(DisplayAnswer.class) 17 | .input((step, data) -> step.answer = data.value1); 18 | } 19 | ... 20 | } 21 | ``` 22 | An event is published to all subscribed workflows via the Workflow Host service, where a data object can be passed to all workflows waiting for event (myEvent) with key 1 as at the current date. 23 | 24 | ```java 25 | Scanner scanner = new Scanner(System.in); 26 | System.out.println("Enter value to publish"); 27 | String inputData = scanner.nextLine(); 28 | 29 | host.publishEvent("myEvent", "1", inputData, new Date()); 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /samples/sample04/src/main/java/net/jworkflow/sample04/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample04; 2 | 3 | import java.util.Arrays; 4 | import net.jworkflow.kernel.interfaces.WorkflowHost; 5 | import net.jworkflow.WorkflowModule; 6 | import java.util.Scanner; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | public class Main { 11 | public static void main(String[] args) throws Exception { 12 | Logger rootLogger = Logger.getLogger(""); 13 | rootLogger.setLevel(Level.SEVERE); 14 | 15 | WorkflowModule module = new WorkflowModule(); 16 | module.build(); 17 | WorkflowHost host = module.getHost(); 18 | 19 | host.registerWorkflow(ForeachWorkflow.class); 20 | 21 | host.start(); 22 | 23 | MyData data = new MyData(); 24 | data.value1 = new String[] {"data item 1", "data item 2"}; 25 | 26 | String id = host.startWorkflow("foreach-sample", 1, data); 27 | System.out.println("started workflow " + id); 28 | 29 | 30 | Scanner scanner = new Scanner(System.in); 31 | scanner.nextLine(); 32 | 33 | System.out.println("shutting down..."); 34 | host.stop(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/WorkflowStepInline.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import com.google.inject.Injector; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import java.util.function.Function; 6 | 7 | public class WorkflowStepInline extends WorkflowStep { 8 | 9 | 10 | public class InlineBody implements StepBody { 11 | 12 | private final Function body; 13 | 14 | public InlineBody(Function body) { 15 | this.body = body; 16 | } 17 | 18 | @Override 19 | public ExecutionResult run(StepExecutionContext context) { 20 | return body.apply(context); 21 | } 22 | 23 | } 24 | 25 | 26 | private final Function body; 27 | 28 | 29 | public WorkflowStepInline(Function body) { 30 | super(InlineBody.class); 31 | this.body = body; 32 | } 33 | 34 | @Override 35 | public StepBody constructBody(Injector injector) throws InstantiationException, IllegalAccessException { 36 | return new InlineBody(body); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /jworkflow.providers.redis/README.md: -------------------------------------------------------------------------------- 1 | # Redis providers for JWorkflow 2 | 3 | Provides support to run multi-node clusters of [JWorkflow](../README.md), by providing a distributed lock manager and/or a shared work queue. 4 | 5 | ## Installing 6 | 7 | ### Using Maven 8 | 9 | ```xml 10 | 11 | 12 | net.jworkflow 13 | jworkflow.providers.redis 14 | >0.5-SNAPSHOT 15 | 16 | 17 | ``` 18 | 19 | ### Using Gradle 20 | 21 | ```Gradle 22 | dependencies { 23 | compile 'net.jworkflow:jworkflow.providers.redis:>0.5-SNAPSHOT' 24 | } 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | ```java 31 | import org.redisson.config.Config; 32 | import net.jworkflow.providers.redis.RedisLockServiceProvider; 33 | ... 34 | ``` 35 | and / or 36 | ```java 37 | import net.jworkflow.providers.redis.RedisQueueServiceProvider; 38 | ... 39 | ``` 40 | 41 | ```java 42 | WorkflowModule module = new WorkflowModule(); 43 | 44 | Config config = new Config(); 45 | config.useSingleServer().setAddress("redis://127.0.0.1:6379"); 46 | 47 | module.useDistibutedLock(new RedisLockServiceProvider(config)); 48 | module.useQueue(new RedisQueueServiceProvider(config)); 49 | 50 | module.build(); 51 | WorkflowHost host = module.getHost(); 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/builders/BaseWorkflowBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.builders; 2 | import net.jworkflow.kernel.builders.DefaultWorkflowBuilder; 3 | import net.jworkflow.kernel.models.WorkflowDefinition; 4 | import net.jworkflow.kernel.models.WorkflowStep; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 8 | 9 | public class BaseWorkflowBuilder { 10 | 11 | protected List steps; 12 | 13 | public BaseWorkflowBuilder() { 14 | this.steps = new ArrayList<>(); 15 | } 16 | 17 | public WorkflowBuilder UseData(Class dataType) { 18 | DefaultWorkflowBuilder result = new DefaultWorkflowBuilder<>(dataType, this.steps); 19 | return result; 20 | } 21 | 22 | public WorkflowDefinition build(String id, int version) { 23 | WorkflowDefinition result = new WorkflowDefinition(); 24 | result.setId(id); 25 | result.setVersion(version); 26 | result.setSteps(steps); 27 | //TODO: DefaultErrorBehavior 28 | 29 | return result; 30 | } 31 | 32 | public void addStep(WorkflowStep step) { 33 | step.setId(this.steps.size()); 34 | this.steps.add(step); 35 | } 36 | 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/CancellableStep.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.time.Instant; 4 | import java.util.Date; 5 | import java.util.function.Function; 6 | import net.jworkflow.kernel.models.ExecutionPointer; 7 | import net.jworkflow.kernel.models.PointerStatus; 8 | import net.jworkflow.kernel.models.WorkflowDefinition; 9 | import net.jworkflow.kernel.models.WorkflowExecutorResult; 10 | import net.jworkflow.kernel.models.WorkflowInstance; 11 | import net.jworkflow.kernel.models.WorkflowStep; 12 | 13 | public class CancellableStep extends WorkflowStep { 14 | 15 | private final Function condition; 16 | 17 | public CancellableStep(Class bodyType, Function condition) { 18 | super(bodyType); 19 | this.condition = condition; 20 | } 21 | 22 | @Override 23 | public void afterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { 24 | Boolean result = condition.apply((TData)workflow.getData()); 25 | if (result) { 26 | executionPointer.active = false; 27 | executionPointer.status = PointerStatus.Complete; 28 | executionPointer.endTimeUtc = Date.from(Instant.now()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /jworkflow.providers.redis/src/main/java/net/jworkflow/providers/redis/RedisLockService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.redis; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.locks.Lock; 6 | import net.jworkflow.kernel.interfaces.LockService; 7 | import org.redisson.Redisson; 8 | import org.redisson.api.RedissonClient; 9 | import org.redisson.config.Config; 10 | 11 | public class RedisLockService implements LockService { 12 | 13 | private final RedissonClient client; 14 | private final Map localLocks; 15 | 16 | public RedisLockService(Config config) { 17 | localLocks = new HashMap<>(); 18 | client = Redisson.create(config); 19 | } 20 | 21 | @Override 22 | public boolean acquireLock(String id) { 23 | Lock lock = client.getLock(id); 24 | if (!lock.tryLock()) 25 | return false; 26 | 27 | localLocks.put(id, lock); 28 | 29 | return true; 30 | } 31 | 32 | @Override 33 | public void releaseLock(String id) { 34 | Lock lock = localLocks.get(id); 35 | if (lock != null) 36 | lock.unlock(); 37 | } 38 | 39 | @Override 40 | public void start() { 41 | 42 | } 43 | 44 | @Override 45 | public void stop() { 46 | client.shutdown(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /samples/sample06/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample06.Main 14 | java 15 | 16 | 17 | 18 | debug 19 | 20 | jar 21 | 22 | 23 | process-classes 24 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 25 | 26 | 27 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath net.jworkflow.sample06.Main 28 | java 29 | true 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /samples/sample07/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | run 5 | 6 | jar 7 | 8 | 9 | process-classes 10 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 11 | 12 | 13 | -classpath %classpath net.jworkflow.sample07.Main 14 | java 15 | 16 | 17 | 18 | debug 19 | 20 | jar 21 | 22 | 23 | process-classes 24 | org.codehaus.mojo:exec-maven-plugin:1.2.1:exec 25 | 26 | 27 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath net.jworkflow.sample07.Main 28 | java 29 | true 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /samples/sample02/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample02 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | ${project.groupId} 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | ${project.groupId} 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | ${project.groupId} 21 | jworkflow.providers.aws 22 | ${project.version} 23 | 24 | 25 | 26 | UTF-8 27 | 1.8 28 | 1.8 29 | 30 | sample02 31 | -------------------------------------------------------------------------------- /docs/03-events.md: -------------------------------------------------------------------------------- 1 | ### Events 2 | 3 | A workflow can also wait for an external event before proceeding. In the following example, the workflow will wait for an event called *"myEvent"* with a key of *0*. Once an external source has fired this event, the workflow will wake up and continue processing, passing the data generated by the event onto the next step. 4 | 5 | ```java 6 | public void build(WorkflowBuilder builder) { 7 | builder 8 | .startsWith(Hello.class) 9 | .waitFor("myEvent", x -> "0") 10 | .output((step, data) -> data.value1 = step.eventData) 11 | .then(DisplayAnswer.class) 12 | .input((step, data) -> step.answer = data.value1); 13 | } 14 | ... 15 | //External events are published via the host 16 | //All workflows that have subscribed to myEvent 0, will be passed "hello" 17 | host.publishEvent("myEvent", "0", "hello"); 18 | ``` 19 | 20 | #### Effective Date 21 | 22 | You can also specify an effective date when waiting for events, which allows you to respond to events that may have already occurred in the past, or only ones that occur after the effective date. 23 | 24 | #### JSON syntax 25 | ```json 26 | { 27 | "id": "MyWaitStep", 28 | "stepType": "net.jworkflow.primitives.WaitFor", 29 | "nextStepId": "...", 30 | "cancelCondition": "...", 31 | "inputs": { 32 | "eventName": "\"Event1\"", 33 | "eventKey": "\"Key1\"", 34 | "effectiveDate": "new Date()" 35 | } 36 | } 37 | ``` -------------------------------------------------------------------------------- /samples/sample02/README.md: -------------------------------------------------------------------------------- 1 | # Passing data between steps sample 2 | 3 | Illustrates how to define a data class for your workflow and wire it's properties up to the inputs and outputs of steps. 4 | 5 | First, we define a class to hold data for our workflow. 6 | 7 | ```java 8 | public class MyData { 9 | public int value1; 10 | public int value2; 11 | public int value3; 12 | } 13 | ``` 14 | 15 | Then we create a step with inputs and outputs, by simply exposing public properties. 16 | 17 | ```java 18 | public class AddNumbers implements StepBody { 19 | 20 | public int number1; 21 | public int number2; 22 | public int answer; 23 | 24 | @Override 25 | public ExecutionResult run(StepExecutionContext context) { 26 | answer = number1 + number2; 27 | return ExecutionResult.next(); 28 | } 29 | } 30 | ``` 31 | 32 | Then we put it all together in a workflow. 33 | 34 | ```java 35 | public class DataWorkflow implements Workflow { 36 | 37 | @Override 38 | public void build(WorkflowBuilder builder) { 39 | builder 40 | .startsWith(AddNumbers.class) 41 | .input((step, data) -> step.number1 = data.value1) 42 | .input((step, data) -> step.number2 = data.value2) 43 | .output((step, data) -> data.value3 = step.answer) 44 | .then(DisplayAnswer.class) 45 | .input((step, data) -> step.answer = data.value3); 46 | } 47 | 48 | ... 49 | } 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/Schedule.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.time.Duration; 4 | import net.jworkflow.kernel.exceptions.CorruptPersistenceDataException; 5 | import net.jworkflow.kernel.interfaces.StepBody; 6 | import net.jworkflow.kernel.models.ExecutionResult; 7 | import net.jworkflow.kernel.models.ScheduleStepData; 8 | import net.jworkflow.kernel.models.StepExecutionContext; 9 | 10 | public class Schedule implements StepBody { 11 | 12 | public Duration duration; 13 | 14 | @Override 15 | public ExecutionResult run(StepExecutionContext context) throws CorruptPersistenceDataException { 16 | 17 | if (context.getPersistenceData() == null) { 18 | return ExecutionResult.sleep(duration, new ScheduleStepData(false)); 19 | } 20 | 21 | if (context.getPersistenceData() instanceof ScheduleStepData) { 22 | ScheduleStepData persistenceData = (ScheduleStepData)context.getPersistenceData(); 23 | 24 | if (!persistenceData.elapsed) { 25 | return ExecutionResult.branch(new Object[1], new ScheduleStepData(true)); 26 | } 27 | 28 | if (context.getWorkflow().isBranchComplete(context.getExecutionPointer().id)) 29 | return ExecutionResult.next(); 30 | 31 | return ExecutionResult.persist(persistenceData); 32 | } 33 | 34 | throw new CorruptPersistenceDataException(); 35 | } 36 | } -------------------------------------------------------------------------------- /scratchpad/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.jworkflow 5 | scratchpad 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | com.google.code.gson 11 | gson 12 | 2.8.5 13 | 14 | 15 | net.jworkflow 16 | sample01 17 | 0.5-SNAPSHOT 18 | 19 | 20 | net.jworkflow 21 | jworkflow.providers.aws 22 | 0.5-SNAPSHOT 23 | 24 | 25 | software.amazon.awssdk 26 | aws-sdk-java 27 | 2.2.0 28 | 29 | 30 | 31 | UTF-8 32 | 1.8 33 | 1.8 34 | 35 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/If.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import net.jworkflow.kernel.exceptions.CorruptPersistenceDataException; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.models.ControlStepData; 6 | import net.jworkflow.kernel.models.ExecutionResult; 7 | import net.jworkflow.kernel.models.StepExecutionContext; 8 | 9 | public class If implements StepBody { 10 | 11 | public boolean condition; 12 | 13 | @Override 14 | public ExecutionResult run(StepExecutionContext context) throws Exception { 15 | 16 | if (context.getPersistenceData() == null) { 17 | if (condition) { 18 | Object[] defaultList = new Object[]{null}; 19 | return ExecutionResult.branch(defaultList, new ControlStepData(true)); 20 | } 21 | else 22 | return ExecutionResult.next(); 23 | } 24 | 25 | if (context.getPersistenceData() instanceof ControlStepData) { 26 | ControlStepData persistenceData = (ControlStepData)context.getPersistenceData(); 27 | 28 | if (persistenceData.childrenActive) { 29 | if (context.getWorkflow().isBranchComplete(context.getExecutionPointer().id)) 30 | return ExecutionResult.next(); 31 | else 32 | return ExecutionResult.persist(persistenceData); 33 | } 34 | } 35 | 36 | throw new CorruptPersistenceDataException(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/sample04/README.md: -------------------------------------------------------------------------------- 1 | # Foreach sample 2 | 3 | Illustrates how to implement a parallel foreach within your workflow. 4 | 5 | 6 | ```java 7 | public class EventsWorkflow implements Workflow { 8 | 9 | @Override 10 | public void build(WorkflowBuilder builder) { 11 | 12 | builder 13 | .startsWith(Hello.class) 14 | .foreach(data -> new String[] { "item 1", "item 2", "item 3" }) 15 | .Do(each -> each 16 | .startsWith(DoSomething.class)) 17 | .then(Goodbye.class); 18 | } 19 | ... 20 | } 21 | ``` 22 | 23 | or get the collectioin from workflow data. 24 | 25 | ```java 26 | public class EventsWorkflow implements Workflow { 27 | 28 | @Override 29 | public void build(WorkflowBuilder builder) { 30 | 31 | builder 32 | .startsWith(Hello.class) 33 | .foreach(data -> data.value1) 34 | .Do(each -> each 35 | .startsWith(DoSomething.class)) 36 | .then(Goodbye.class); 37 | } 38 | ... 39 | } 40 | ``` 41 | 42 | Access the iteration item from the step execution context 43 | 44 | ```java 45 | public class DoSomething implements StepBody { 46 | 47 | @Override 48 | public ExecutionResult run(StepExecutionContext context) { 49 | System.out.println("Doing something with " + context.getItem()); 50 | return ExecutionResult.next(); 51 | } 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/SingleNodeQueueService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | import net.jworkflow.kernel.models.QueueType; 3 | import net.jworkflow.kernel.interfaces.QueueService; 4 | import com.google.inject.Singleton; 5 | import java.util.concurrent.ConcurrentLinkedQueue; 6 | 7 | @Singleton 8 | public class SingleNodeQueueService implements QueueService{ 9 | 10 | private final ConcurrentLinkedQueue workflowQueue; 11 | private final ConcurrentLinkedQueue eventQueue; 12 | 13 | public SingleNodeQueueService() { 14 | workflowQueue = new ConcurrentLinkedQueue<>(); 15 | eventQueue = new ConcurrentLinkedQueue<>(); 16 | } 17 | 18 | @Override 19 | public synchronized void queueForProcessing(QueueType type, String id) { 20 | switch (type) { 21 | case WORKFLOW: 22 | workflowQueue.add(id); 23 | break; 24 | case EVENT: 25 | eventQueue.add(id); 26 | break; 27 | } 28 | } 29 | 30 | @Override 31 | public synchronized String dequeueForProcessing(QueueType type) { 32 | switch (type) { 33 | case WORKFLOW: 34 | return workflowQueue.poll(); 35 | case EVENT: 36 | return eventQueue.poll(); 37 | default: 38 | return null; 39 | } 40 | } 41 | 42 | @Override 43 | public boolean isDequeueBlocking() { 44 | return false; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /samples/sample08/src/main/resources/workflow5.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "test-workflow", 3 | "version": 1, 4 | "steps": [ 5 | { 6 | "id": "hello", 7 | "stepType": "net.jworkflow.sample08.steps.Hello", 8 | "nextStepId": "mySaga" 9 | }, 10 | { 11 | "id": "mySaga", 12 | "stepType": "net.jworkflow.primitives.Sequence", 13 | "nextStepId": "bye", 14 | "saga": true, 15 | "thenDo": [ 16 | [ 17 | { 18 | "id": "do1", 19 | "stepType": "net.jworkflow.sample08.steps.Task1", 20 | "nextStepId": "do2", 21 | "compensateWith": [ 22 | { 23 | "id": "undo1", 24 | "stepType": "net.jworkflow.sample08.steps.UndoTask1" 25 | } 26 | ] 27 | }, 28 | { 29 | "id": "do2", 30 | "stepType": "net.jworkflow.sample08.steps.Task2", 31 | "compensateWith": [ 32 | { 33 | "id": "undo2", 34 | "stepType": "net.jworkflow.sample08.steps.UndoTask2" 35 | } 36 | ] 37 | }, 38 | { 39 | "id": "do3", 40 | "stepType": "net.jworkflow.sample08.steps.Task3", 41 | "compensateWith": [ 42 | { 43 | "id": "undo3", 44 | "stepType": "net.jworkflow.sample08.steps.UndoTask3" 45 | } 46 | ] 47 | } 48 | ] 49 | ] 50 | }, 51 | { 52 | "id": "bye", 53 | "stepType": "net.jworkflow.sample08.steps.Goodbye" 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /jworkflow.providers.redis/src/main/java/net/jworkflow/providers/redis/RedisQueueService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.redis; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Queue; 6 | import java.util.logging.Logger; 7 | import net.jworkflow.kernel.interfaces.QueueService; 8 | import net.jworkflow.kernel.models.QueueType; 9 | import org.redisson.Redisson; 10 | import org.redisson.api.RedissonClient; 11 | import org.redisson.config.Config; 12 | 13 | public class RedisQueueService implements QueueService { 14 | 15 | private final RedissonClient client; 16 | private final String queuePrefix = "jworkflow-"; 17 | private final Map queues; 18 | private final int waitTime = 5; 19 | 20 | public RedisQueueService(Config config) { 21 | client = Redisson.create(config); 22 | 23 | queues = new HashMap<>(); 24 | queues.put(QueueType.WORKFLOW, createQueue(QueueType.WORKFLOW)); 25 | queues.put(QueueType.EVENT, createQueue(QueueType.EVENT)); 26 | } 27 | 28 | @Override 29 | public void queueForProcessing(QueueType type, String id) { 30 | queues.get(type).add(id); 31 | } 32 | 33 | @Override 34 | public String dequeueForProcessing(QueueType type) { 35 | return (String)queues.get(type).poll(); 36 | } 37 | 38 | @Override 39 | public boolean isDequeueBlocking() { 40 | return false; 41 | } 42 | 43 | private Queue createQueue(QueueType type) { 44 | String queueName = queuePrefix + type.toString(); 45 | return client.getQueue(queueName); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /samples/sample02/src/main/java/net/jworkflow/sample02/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample02; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowHost; 4 | import net.jworkflow.WorkflowModule; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import net.jworkflow.providers.aws.DynamoDBPersistenceProvider; 9 | import software.amazon.awssdk.regions.Region; 10 | 11 | public class Main { 12 | public static void main(String[] args) throws Exception { 13 | 14 | Logger rootLogger = Logger.getLogger(""); 15 | rootLogger.setLevel(Level.SEVERE); 16 | 17 | WorkflowModule module = new WorkflowModule(); 18 | 19 | //module.useDistibutedLock(new DynamoDBLockProvider(Region.US_WEST_1, "jworkflowLocks")); 20 | module.usePersistence(new DynamoDBPersistenceProvider(Region.US_WEST_1, "j-sample2")); 21 | 22 | module.build(); 23 | WorkflowHost host = module.getHost(); 24 | 25 | host.registerWorkflow(DataWorkflow.class); 26 | 27 | host.start(); 28 | MyData data = new MyData(); 29 | data.value1 = 2; 30 | data.value2 = 3; 31 | 32 | String id = host.startWorkflow("data-workflow", 1, data); 33 | System.out.println("started workflow " + id); 34 | 35 | 36 | Scanner scanner = new Scanner(System.in); 37 | scanner.nextLine(); 38 | 39 | System.out.println("shutting down..."); 40 | host.stop(); 41 | 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scratchpad/src/main/java/com/jworkflow/scratchpad/Main.java: -------------------------------------------------------------------------------- 1 | package com.jworkflow.scratchpad; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.function.BiConsumer; 7 | import java.util.function.Consumer; 8 | import java.util.function.Function; 9 | import java.util.function.Predicate; 10 | import net.jworkflow.providers.aws.DynamoDBLockService; 11 | import net.jworkflow.sample01.HelloWorkflow; 12 | import software.amazon.awssdk.regions.Region; 13 | 14 | public class Main { 15 | public static void main(String[] args) throws Exception { 16 | 17 | Function f1 = (o) -> o.id; 18 | Function f2 = (o) -> o.getPid(); 19 | 20 | //Function f3 = TestData::getPid; 21 | 22 | 23 | //Consumer f4 = TestData::setPid; 24 | 25 | BiConsumer f3 = (o, v) -> o.setPid(v); 26 | 27 | 28 | TestData d1 = new TestData(); 29 | d1.id = 1; 30 | d1.name = "hi"; 31 | d1.setPid(5); 32 | 33 | TestData.class.getField("id").set(d1, 2); 34 | //TestData.class.getMethod("setPid").set(d1, 8); 35 | 36 | f3.accept(d1, 11); 37 | 38 | Object v1 = f1.apply(d1); 39 | Object v2 = f2.apply(d1); 40 | 41 | 42 | System.out.println(String.valueOf(v1)); 43 | System.out.println(String.valueOf(v2)); 44 | 45 | 46 | 47 | 48 | 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/primitives/While.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.primitives; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.function.Function; 7 | import net.jworkflow.kernel.exceptions.CorruptPersistenceDataException; 8 | import net.jworkflow.kernel.interfaces.StepBody; 9 | import net.jworkflow.kernel.models.ControlStepData; 10 | import net.jworkflow.kernel.models.ExecutionPointer; 11 | import net.jworkflow.kernel.models.ExecutionResult; 12 | import net.jworkflow.kernel.models.StepExecutionContext; 13 | 14 | public class While implements StepBody { 15 | 16 | public boolean condition; 17 | 18 | @Override 19 | public ExecutionResult run(StepExecutionContext context) throws Exception { 20 | 21 | if (context.getPersistenceData() == null) { 22 | if (condition) { 23 | Object[] defaultList = new Object[]{null}; 24 | return ExecutionResult.branch(defaultList, new ControlStepData(true)); 25 | } 26 | else 27 | return ExecutionResult.next(); 28 | } 29 | 30 | if (context.getPersistenceData() instanceof ControlStepData) { 31 | ControlStepData persistenceData = (ControlStepData)context.getPersistenceData(); 32 | 33 | if (persistenceData.childrenActive) { 34 | if (context.getWorkflow().isBranchComplete(context.getExecutionPointer().id)) 35 | return ExecutionResult.persist(null); 36 | else 37 | return ExecutionResult.persist(persistenceData); 38 | } 39 | } 40 | 41 | throw new CorruptPersistenceDataException(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/WorkflowDefinition.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public final class WorkflowDefinition { 8 | private String id; 9 | private int version; 10 | private String description; 11 | private List steps; 12 | private Class dataType; 13 | 14 | public WorkflowDefinition() { 15 | setSteps(new ArrayList<>()); 16 | } 17 | 18 | 19 | public String getId() { 20 | return id; 21 | } 22 | 23 | public void setId(String id) { 24 | this.id = id; 25 | } 26 | 27 | public int getVersion() { 28 | return version; 29 | } 30 | 31 | public void setVersion(int version) { 32 | this.version = version; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public void setDescription(String description) { 40 | this.description = description; 41 | } 42 | 43 | public List getSteps() { 44 | return steps; 45 | } 46 | 47 | public void setSteps(List steps) { 48 | this.steps = steps; 49 | } 50 | 51 | /** 52 | * @return the dataType 53 | */ 54 | public Class getDataType() { 55 | return dataType; 56 | } 57 | 58 | /** 59 | * @param dataType the dataType to set 60 | */ 61 | public void setDataType(Class dataType) { 62 | this.dataType = dataType; 63 | } 64 | 65 | public WorkflowStep findStep(int findId) { 66 | for (WorkflowStep step: steps) { 67 | if (step.getId() == findId) 68 | return step; 69 | } 70 | return null; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/Scenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.Workflow; 4 | import net.jworkflow.kernel.interfaces.WorkflowHost; 5 | import net.jworkflow.kernel.models.WorkflowInstance; 6 | import net.jworkflow.kernel.models.WorkflowStatus; 7 | import net.jworkflow.WorkflowModule; 8 | import net.jworkflow.kernel.interfaces.PersistenceService; 9 | 10 | public abstract class Scenario { 11 | 12 | protected String WorkflowDefId; 13 | protected int WorkflowVersion; 14 | protected WorkflowHost host; 15 | protected PersistenceService persistence; 16 | 17 | public Scenario() { 18 | this.WorkflowDefId = "scenario"; 19 | this.WorkflowVersion = 1; 20 | } 21 | 22 | protected void setupWorkflow() { 23 | WorkflowModule module = new WorkflowModule(); 24 | module.build(); 25 | host = module.getHost(); 26 | persistence = module.getPersistenceProvider(); 27 | } 28 | 29 | protected WorkflowInstance runWorkflow(Workflow workflow, Object data) throws Exception{ 30 | setupWorkflow(); 31 | 32 | host.registerWorkflow(workflow); 33 | 34 | host.start(); 35 | 36 | String id = host.startWorkflow(WorkflowDefId, WorkflowVersion, data); 37 | 38 | WorkflowInstance instance = persistence.getWorkflowInstance(id); 39 | int counter = 0; 40 | while ((instance.getStatus() == WorkflowStatus.RUNNABLE) && (counter < 100)) { 41 | Thread.sleep(100); 42 | instance = persistence.getWorkflowInstance(id); 43 | counter++; 44 | } 45 | 46 | host.stop(); 47 | 48 | return instance; 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # JWorkflow 2 | 3 | JWorkflow is a light weight workflow library for Java. It supports pluggable persistence and concurrency providers to allow for multi-node clusters. 4 | 5 | ## Installing 6 | 7 | ### Using Maven 8 | 9 | Add `jworkflow` to your POM file as a dependency. 10 | 11 | ```xml 12 | 13 | 14 | net.jworkflow 15 | jworkflow 16 | 0.5-SNAPSHOT 17 | 18 | 19 | ``` 20 | 21 | ### Using Gradle 22 | 23 | ```Gradle 24 | dependencies { 25 | compile 'net.jworkflow:jworkflow:0.5-SNAPSHOT' 26 | } 27 | ``` 28 | 29 | ## Tutorial 30 | 31 | * [Basic Concepts](01-basic-concepts.md) 32 | * [Passing Data](02-data.md) 33 | * [Reponding to external events](03-events.md) 34 | * [Error handling](04-error-handling.md) 35 | * [Control structures](05-control-structures.md) 36 | * [Saga transactions](06-sagas.md) 37 | * [Schedule and recur](07-schedule.md) 38 | * [Defining workflows in JSON](08-json.md) 39 | 40 | 41 | ## Samples 42 | 43 | * [Hello World](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample01) 44 | * [Passing Data](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample02) 45 | * [If condition](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample06) 46 | * [Responding to external events](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample03) 47 | * [Parallel ForEach](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample04) 48 | * [While loop](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample05) 49 | * [Saga Transactions](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample07) 50 | * [JSON Workflows](https://github.com/danielgerlag/jworkflow/tree/master/samples/sample08) 51 | -------------------------------------------------------------------------------- /samples/sample03/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | net.jworkflow 5 | sample03 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | ${project.groupId} 11 | jworkflow 12 | ${project.version} 13 | 14 | 15 | ${project.groupId} 16 | jworkflow.providers.mongodb 17 | ${project.version} 18 | 19 | 20 | ${project.groupId} 21 | jworkflow.providers.redis 22 | ${project.version} 23 | 24 | 25 | ${project.groupId} 26 | jworkflow.providers.aws 27 | ${project.version} 28 | 29 | 30 | ${project.groupId} 31 | jworkflow.providers.rabbitmq 32 | ${project.version} 33 | 34 | 35 | 36 | UTF-8 37 | 1.8 38 | 1.8 39 | 40 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/builders/DefaultParallelStepBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.builders; 2 | 3 | import net.jworkflow.kernel.interfaces.ParallelStepBuilder; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import net.jworkflow.kernel.interfaces.StepBuilder; 6 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 7 | import net.jworkflow.kernel.interfaces.WorkflowBuilderConsumer; 8 | import net.jworkflow.kernel.models.WorkflowStep; 9 | import net.jworkflow.primitives.Sequence; 10 | 11 | public class DefaultParallelStepBuilder implements ParallelStepBuilder { 12 | 13 | private final WorkflowBuilder workflowBuilder; 14 | private final WorkflowStep step; 15 | private final StepBuilder referenceBuilder; 16 | private final StepBuilder stepBuilder; 17 | 18 | public DefaultParallelStepBuilder(WorkflowBuilder workflowBuilder, StepBuilder stepBuilder, StepBuilder referenceBuilder) { 19 | this.workflowBuilder = workflowBuilder; 20 | this.step = stepBuilder.getStep(); 21 | this.referenceBuilder = referenceBuilder; 22 | this.stepBuilder = stepBuilder; 23 | } 24 | 25 | @Override 26 | public ParallelStepBuilder Do(WorkflowBuilderConsumer consumer) { 27 | int lastStep = workflowBuilder.getLastStep(); 28 | consumer.accept(workflowBuilder); 29 | 30 | if (lastStep == workflowBuilder.getLastStep()) 31 | throw new UnsupportedOperationException("Empty Do block not supported"); 32 | 33 | step.addChild(lastStep + 1); //TODO: make more elegant 34 | 35 | return this; 36 | } 37 | 38 | @Override 39 | public StepBuilder Join() { 40 | return referenceBuilder; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /samples/sample08/src/main/java/net/jworkflow/sample08/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample08; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import net.jworkflow.kernel.interfaces.WorkflowHost; 9 | import net.jworkflow.WorkflowModule; 10 | import java.util.Scanner; 11 | import java.util.logging.Level; 12 | import java.util.logging.Logger; 13 | 14 | public class Main { 15 | public static void main(String[] args) throws Exception { 16 | Logger rootLogger = Logger.getLogger(""); 17 | rootLogger.setLevel(Level.SEVERE); 18 | 19 | String str = readResource("workflow1.json"); 20 | WorkflowModule module = new WorkflowModule(); 21 | module.build(); 22 | WorkflowHost host = module.getHost(); 23 | host.registerWorkflowFromJson(str); 24 | 25 | host.start(); 26 | 27 | MyData data = new MyData(); 28 | data.value1 = 2; 29 | data.value2 = 3; 30 | data.collection1 = new Object[2]; 31 | String id = host.startWorkflow("test-workflow", 1, data); 32 | System.out.println("started workflow " + id); 33 | Scanner scanner = new Scanner(System.in); 34 | scanner.nextLine(); 35 | 36 | System.out.println("shutting down..."); 37 | host.stop(); 38 | } 39 | 40 | private static String readResource(String name) throws IOException, UnsupportedEncodingException { 41 | 42 | URL url = Main.class.getClassLoader().getResource(name); 43 | System.out.println(url.getFile()); 44 | URLConnection conn = url.openConnection(); 45 | byte[] data; 46 | try (InputStream stream = (InputStream)conn.getContent()) { 47 | data = new byte[(int) conn.getContentLength()]; 48 | stream.read(data); 49 | } 50 | String str = new String(data, "UTF-8"); 51 | return str; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/StepExecutionContext.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | public class StepExecutionContext { 4 | private Object persistenceData; 5 | private WorkflowStep step; 6 | private WorkflowInstance workflow; 7 | private Object item; 8 | private ExecutionPointer executionPointer; 9 | 10 | /** 11 | * @return the persistenceData 12 | */ 13 | public Object getPersistenceData() { 14 | return persistenceData; 15 | } 16 | 17 | /** 18 | * @param persistenceData the persistenceData to set 19 | */ 20 | public void setPersistenceData(Object persistenceData) { 21 | this.persistenceData = persistenceData; 22 | } 23 | 24 | /** 25 | * @return the step 26 | */ 27 | public WorkflowStep getStep() { 28 | return step; 29 | } 30 | 31 | /** 32 | * @param step the step to set 33 | */ 34 | public void setStep(WorkflowStep step) { 35 | this.step = step; 36 | } 37 | 38 | /** 39 | * @return the workflow 40 | */ 41 | public WorkflowInstance getWorkflow() { 42 | return workflow; 43 | } 44 | 45 | /** 46 | * @param workflow the workflow to set 47 | */ 48 | public void setWorkflow(WorkflowInstance workflow) { 49 | this.workflow = workflow; 50 | } 51 | 52 | /** 53 | * @return the item 54 | */ 55 | public Object getItem() { 56 | return item; 57 | } 58 | 59 | /** 60 | * @param item the item to set 61 | */ 62 | public void setItem(Object item) { 63 | this.item = item; 64 | } 65 | 66 | /** 67 | * @return the executionPointer 68 | */ 69 | public ExecutionPointer getExecutionPointer() { 70 | return executionPointer; 71 | } 72 | 73 | /** 74 | * @param executionPointer the executionPointer to set 75 | */ 76 | public void setExecutionPointer(ExecutionPointer executionPointer) { 77 | this.executionPointer = executionPointer; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/README.md: -------------------------------------------------------------------------------- 1 | # AWS providers for JWorkflow 2 | 3 | * Provides support to persist long running workflows for [JWorkflow](../README.md) backed by Amazon DynamoDB 4 | * Provides support to run multi-node clusters of [JWorkflow](../README.md), by providing a distributed lock manager (via DynamoDB) and/or a shared work queue (via Simple Queue Service). 5 | 6 | ## Installing 7 | 8 | ### Using Maven 9 | 10 | ```xml 11 | 12 | 13 | net.jworkflow 14 | jworkflow.providers.aws 15 | >0.5-SNAPSHOT 16 | 17 | 18 | ``` 19 | 20 | ### Using Gradle 21 | 22 | ```Gradle 23 | dependencies { 24 | compile 'net.jworkflow:jworkflow.providers.aws:>0.5-SNAPSHOT' 25 | } 26 | ``` 27 | 28 | ## Usage for Persistence 29 | 30 | 31 | ```java 32 | import software.amazon.awssdk.regions.Region; 33 | import net.jworkflow.providers.aws.DynamoDBPersistenceProvider; 34 | ... 35 | ... 36 | 37 | WorkflowModule module = new WorkflowModule(); 38 | 39 | module.usePersistence(new DynamoDBPersistenceProvider(Region.US_WEST_1, "table-prefix")); 40 | 41 | module.build(); 42 | WorkflowHost host = module.getHost(); 43 | 44 | ``` 45 | 46 | 47 | ## Usage for Shared Work Queue 48 | 49 | ```java 50 | import software.amazon.awssdk.regions.Region; 51 | import net.jworkflow.providers.aws.SQSProvider; 52 | ... 53 | ... 54 | 55 | WorkflowModule module = new WorkflowModule(); 56 | 57 | module.useQueue(new SQSProvider(Region.US_WEST_1)); 58 | 59 | module.build(); 60 | WorkflowHost host = module.getHost(); 61 | 62 | ``` 63 | 64 | 65 | ## Usage for Distributed Lock Manager 66 | 67 | ```java 68 | import software.amazon.awssdk.regions.Region; 69 | import net.jworkflow.providers.aws.DynamoDBLockProvider; 70 | ... 71 | ... 72 | 73 | WorkflowModule module = new WorkflowModule(); 74 | 75 | module.useDistibutedLock(new DynamoDBLockProvider(Region.US_WEST_1, "jworkflowLockTable")); //DynamoDB table name of your choice 76 | 77 | module.build(); 78 | WorkflowHost host = module.getHost(); 79 | 80 | ``` 81 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/QueueWorker.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import net.jworkflow.kernel.interfaces.QueueService; 6 | import net.jworkflow.kernel.models.QueueType; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | import net.jworkflow.kernel.interfaces.BackgroundService; 10 | 11 | public abstract class QueueWorker implements BackgroundService { 12 | 13 | protected final QueueService queueProvider; 14 | protected final Logger logger; 15 | protected final long idleTime = 1000; 16 | protected final ExecutorService workerPool; 17 | protected boolean active; 18 | 19 | public QueueWorker(QueueService queueProvider, Logger logger) { 20 | this.queueProvider = queueProvider; 21 | this.logger = logger; 22 | this.active = false; 23 | this.workerPool = Executors.newCachedThreadPool(); 24 | } 25 | 26 | protected abstract int getThreadCount(); 27 | protected abstract QueueType getQueueType(); 28 | protected abstract void executeItem(String item) throws Exception; 29 | 30 | public void run() { 31 | while (active) { 32 | try { 33 | String item = queueProvider.dequeueForProcessing(getQueueType()); 34 | 35 | if (item == null) { 36 | if (!queueProvider.isDequeueBlocking()) { 37 | Thread.sleep(idleTime); 38 | } 39 | continue; 40 | } 41 | executeItem(item); 42 | } 43 | catch (Exception ex) { 44 | logger.log(Level.SEVERE, ex.toString()); 45 | } 46 | } 47 | } 48 | 49 | @Override 50 | public void start() { 51 | active = true; 52 | for (int i = 0; i < getThreadCount(); i++) { 53 | workerPool.submit(() -> run()); 54 | } 55 | } 56 | 57 | @Override 58 | public void stop() { 59 | active = false; 60 | workerPool.shutdown(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/BasicWorkflowScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.interfaces.Workflow; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | import net.jworkflow.kernel.models.WorkflowInstance; 8 | import net.jworkflow.kernel.models.WorkflowStatus; 9 | import org.junit.Test; 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNotNull; 12 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 13 | 14 | public class BasicWorkflowScenario extends Scenario{ 15 | 16 | private static int step1Ticker = 0; 17 | private static int step2Ticker = 0; 18 | 19 | static class Step1 implements StepBody { 20 | @Override 21 | public ExecutionResult run(StepExecutionContext context) { 22 | step1Ticker++; 23 | return ExecutionResult.next(); 24 | } 25 | } 26 | 27 | static class Step2 implements StepBody { 28 | @Override 29 | public ExecutionResult run(StepExecutionContext context) { 30 | step2Ticker++; 31 | return ExecutionResult.next(); 32 | } 33 | } 34 | 35 | class BasicWorkflow implements Workflow { 36 | 37 | @Override 38 | public String getId() { 39 | return "scenario"; 40 | } 41 | 42 | @Override 43 | public Class getDataType() { 44 | return Object.class; 45 | } 46 | 47 | @Override 48 | public int getVersion() { 49 | return 1; 50 | } 51 | 52 | @Override 53 | public void build(WorkflowBuilder builder) { 54 | builder 55 | .startsWith(Step1.class) 56 | .then(Step2.class); 57 | } 58 | } 59 | 60 | @Test 61 | public void test() throws Exception { 62 | WorkflowInstance result = runWorkflow(new BasicWorkflow(), null); 63 | 64 | assertEquals(WorkflowStatus.COMPLETE, result.getStatus()); 65 | assertEquals(1, step1Ticker); 66 | assertEquals(1, step2Ticker); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/EventScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import java.time.Instant; 4 | import java.util.Date; 5 | import net.jworkflow.kernel.interfaces.Workflow; 6 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 7 | import net.jworkflow.kernel.models.ExecutionResult; 8 | import net.jworkflow.kernel.services.abstractions.WorkflowTest; 9 | import org.junit.Test; 10 | import static org.junit.Assert.*; 11 | 12 | public class EventScenario extends WorkflowTest { 13 | 14 | private static int step1Ticker = 0; 15 | private static int step2Ticker = 0; 16 | 17 | class MyData { 18 | public int value1; 19 | } 20 | 21 | class EventWorkflow implements Workflow { 22 | 23 | @Override 24 | public String getId() { 25 | return "scenario"; 26 | } 27 | 28 | @Override 29 | public Class getDataType() { 30 | return Object.class; 31 | } 32 | 33 | @Override 34 | public int getVersion() { 35 | return 1; 36 | } 37 | 38 | @Override 39 | public void build(WorkflowBuilder builder) { 40 | builder 41 | .startsWithAction(context -> step1Ticker++) 42 | .waitFor("event", data -> "key") 43 | .output((step, data) -> data.value1 = (int)step.eventData) 44 | .thenAction(context -> step2Ticker++); 45 | } 46 | } 47 | 48 | @Override 49 | protected Workflow getWorkflow() { 50 | return new EventWorkflow(); 51 | } 52 | 53 | @Test 54 | public void Scenario() throws Exception { 55 | setup(); 56 | MyData data = new MyData(); 57 | String workflowId = startWorkflow(data); 58 | 59 | waitForEventSubscription("event", "key"); 60 | assertEquals(1, step1Ticker); 61 | assertEquals(0, step2Ticker); 62 | host.publishEvent("event", "key", 7, new Date()); 63 | waitForWorkflowToComplete(workflowId); 64 | 65 | assertEquals(1, step1Ticker); 66 | assertEquals(1, step2Ticker); 67 | assertEquals(7, data.value1); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/ExecutionPointerCollection.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Optional; 11 | import java.util.function.Predicate; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | public final class ExecutionPointerCollection implements Iterable, Serializable { 16 | 17 | private final Map data; 18 | private final Map> stackMap; 19 | 20 | public ExecutionPointerCollection() { 21 | data = new HashMap<>(); 22 | stackMap = new HashMap<>(); 23 | } 24 | 25 | public ExecutionPointerCollection(Collection seed) { 26 | stackMap = new HashMap<>(seed.size()); 27 | data = new HashMap<>(seed.size()); 28 | seed.stream().forEach((p) -> { 29 | add(p); 30 | }); 31 | } 32 | 33 | public void add(ExecutionPointer pointer) { 34 | data.put(pointer.id, pointer); 35 | pointer.callStack.forEach(frame -> { 36 | if (!stackMap.containsKey(frame)) { 37 | stackMap.put(frame, new ArrayList<>()); 38 | } 39 | stackMap.get(frame).add(pointer); 40 | }); 41 | } 42 | 43 | public ExecutionPointer findById(String id) { 44 | return data.get(id); 45 | } 46 | 47 | public Optional findOne(Predicate filter) { 48 | return data.values().stream().filter(filter).findFirst(); 49 | } 50 | 51 | public List findMany(Predicate filter) { 52 | return data.values().stream().filter(filter).collect(Collectors.toList()); 53 | } 54 | 55 | public Stream streamMany(Predicate filter) { 56 | return data.values().stream().filter(filter); 57 | } 58 | 59 | public Stream stream() { 60 | return data.values().stream(); 61 | } 62 | 63 | @Override 64 | public Iterator iterator() { 65 | return data.values().iterator(); 66 | } 67 | 68 | public Collection findByStackFrame(String frameId) { 69 | if (!stackMap.containsKey(frameId)) 70 | return new ArrayList<>(); 71 | 72 | return stackMap.get(frameId); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /samples/sample03/src/main/java/net/jworkflow/sample03/Main.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample03; 2 | 3 | import com.rabbitmq.client.ConnectionFactory; 4 | import net.jworkflow.kernel.interfaces.WorkflowHost; 5 | import net.jworkflow.WorkflowModule; 6 | import net.jworkflow.providers.mongodb.MongoPersistenceService; 7 | import java.util.Date; 8 | import java.util.Scanner; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import net.jworkflow.providers.aws.DynamoDBLockProvider; 12 | import net.jworkflow.providers.aws.DynamoDBPersistenceProvider; 13 | import net.jworkflow.providers.aws.SQSProvider; 14 | import net.jworkflow.providers.rabbitmq.RabbitMQProvider; 15 | import net.jworkflow.providers.redis.RedisLockServiceProvider; 16 | import net.jworkflow.providers.redis.RedisQueueServiceProvider; 17 | import org.redisson.config.Config; 18 | import software.amazon.awssdk.regions.Region; 19 | 20 | public class Main { 21 | public static void main(String[] args) throws Exception { 22 | 23 | Logger rootLogger = Logger.getLogger(""); 24 | rootLogger.setLevel(Level.SEVERE); 25 | 26 | WorkflowModule module = new WorkflowModule(); 27 | //module.usePersistence(MongoPersistenceService.configure("mongodb://localhost:27017/jworkflow")); 28 | 29 | //Config config = new Config(); 30 | //config.useSingleServer().setAddress("redis://127.0.0.1:6379"); 31 | //module.useDistibutedLock(new RedisLockServiceProvider(config)); 32 | //module.useQueue(new RedisQueueServiceProvider(config)); 33 | 34 | //module.useQueue(new SQSProvider(Region.US_WEST_1)); 35 | //module.useDistibutedLock(new DynamoDBLockProvider(Region.US_WEST_1, "jworkflowLocks")); 36 | //module.usePersistence(new DynamoDBPersistenceProvider(Region.US_WEST_1, "test5")); 37 | 38 | module.build(); 39 | WorkflowHost host = module.getHost(); 40 | 41 | host.registerWorkflow(EventsWorkflow.class); 42 | 43 | host.start(); 44 | MyData data = new MyData(); 45 | 46 | String id = host.startWorkflow("events-workflow", 1, data); 47 | System.out.println("started workflow " + id); 48 | 49 | Scanner scanner = new Scanner(System.in); 50 | System.out.println("Enter value to publish"); 51 | String inputData = scanner.nextLine(); 52 | 53 | host.publishEvent("myEvent", "1", inputData, new Date()); 54 | 55 | scanner.nextLine(); 56 | System.out.println("shutting down..."); 57 | host.stop(); 58 | 59 | } 60 | } -------------------------------------------------------------------------------- /docs/02-data.md: -------------------------------------------------------------------------------- 1 | ### Passing data between steps 2 | 3 | Each step is intended to be a black-box, therefore they support inputs and outputs. These inputs and outputs can be mapped to a data class that defines the custom data relevant to each workflow instance. 4 | 5 | The following sample shows how to define inputs and outputs on a step, it then shows how define a workflow with a typed class for internal data and how to map the inputs and outputs to properties on the custom data class. 6 | 7 | ```java 8 | //Our workflow step with inputs and outputs 9 | public class AddNumbers implements StepBody { 10 | 11 | public int number1; 12 | public int number2; 13 | public int answer; 14 | 15 | @Override 16 | public ExecutionResult run(StepExecutionContext context) { 17 | answer = number1 + number2; 18 | return ExecutionResult.next(); 19 | } 20 | } 21 | 22 | //Our class to define the internal data of our workflow 23 | public class MyData { 24 | public int value1; 25 | public int value2; 26 | public int value3; 27 | } 28 | 29 | //Our workflow definition with strongly typed internal data and mapped inputs & outputs 30 | public class DataWorkflow implements Workflow { 31 | 32 | @Override 33 | public String getId() { 34 | return "data-workflow"; 35 | } 36 | 37 | @Override 38 | public Class getDataType() { 39 | return MyData.class; 40 | } 41 | 42 | @Override 43 | public int getVersion() { 44 | return 1; 45 | } 46 | 47 | @Override 48 | public void build(WorkflowBuilder builder) { 49 | 50 | builder 51 | .startsWith(AddNumbers.class) 52 | .input((step, data) -> step.number1 = data.value1) 53 | .input((step, data) -> step.number2 = data.value2) 54 | .output((step, data) -> data.value3 = step.answer) 55 | .then(DisplayAnswer.class) 56 | .input((step, data) -> step.answer = data.value3); 57 | } 58 | } 59 | ``` 60 | 61 | or as a JSON definition 62 | ```json 63 | { 64 | "id": "data-workflow", 65 | "version": 1, 66 | "dataType": "com.application.MyData", 67 | "steps": [ 68 | { 69 | "id": "add", 70 | "stepType": "com.application.AddNumbers", 71 | "nextStepId": "display", 72 | "inputs": { 73 | "value1": "data.value1", 74 | "value2": "data.value2" 75 | }, 76 | "outputs": { 77 | "value3": "step.answer" 78 | } 79 | }, 80 | { 81 | "id": "display", 82 | "stepType": "com.application.DisplayAnswer", 83 | "inputs": { 84 | "answer": "data.value3" 85 | } 86 | } 87 | ] 88 | } 89 | ``` -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/ForeachScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.interfaces.Workflow; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | import net.jworkflow.kernel.models.WorkflowInstance; 8 | import net.jworkflow.kernel.models.WorkflowStatus; 9 | import org.junit.Test; 10 | import static org.junit.Assert.assertEquals; 11 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 12 | 13 | public class ForeachScenario extends Scenario{ 14 | 15 | private static int step1Ticker = 0; 16 | private static int step2Ticker = 0; 17 | private static int step3Ticker = 0; 18 | private static int checkSum = 0; 19 | 20 | public class MyData { 21 | public Object[] value1; 22 | } 23 | 24 | static class DoSomething implements StepBody { 25 | 26 | @Override 27 | public ExecutionResult run(StepExecutionContext context) { 28 | step2Ticker++; 29 | checkSum += (int)context.getItem(); 30 | return ExecutionResult.next(); 31 | } 32 | } 33 | 34 | class ScenarioWorkflow implements Workflow { 35 | 36 | @Override 37 | public String getId() { 38 | return "scenario"; 39 | } 40 | 41 | @Override 42 | public Class getDataType() { 43 | return Object.class; 44 | } 45 | 46 | @Override 47 | public int getVersion() { 48 | return 1; 49 | } 50 | 51 | @Override 52 | public void build(WorkflowBuilder builder) { 53 | builder 54 | .startsWith(context -> { 55 | step1Ticker++; 56 | return ExecutionResult.next(); 57 | }) 58 | .foreach(data -> data.value1) 59 | .Do(each -> each 60 | .startsWith(DoSomething.class)) 61 | .then(context -> { 62 | step3Ticker++; 63 | return ExecutionResult.next(); 64 | }); 65 | } 66 | } 67 | 68 | @Test 69 | public void test() throws Exception { 70 | MyData data = new MyData(); 71 | data.value1 = new Object[3]; 72 | data.value1[0] = 2; 73 | data.value1[1] = 3; 74 | data.value1[2] = 2; 75 | WorkflowInstance result = runWorkflow(new ScenarioWorkflow(), data); 76 | 77 | assertEquals(WorkflowStatus.COMPLETE, result.getStatus()); 78 | assertEquals(1, step1Ticker); 79 | assertEquals(3, step2Ticker); 80 | assertEquals(1, step3Ticker); 81 | assertEquals(7, checkSum); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/DefaultExecutionPointerFactory.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | 3 | import com.google.inject.Singleton; 4 | import java.util.Stack; 5 | import java.util.UUID; 6 | import net.jworkflow.kernel.models.*; 7 | import net.jworkflow.kernel.interfaces.ExecutionPointerFactory; 8 | 9 | @Singleton 10 | public class DefaultExecutionPointerFactory implements ExecutionPointerFactory { 11 | 12 | @Override 13 | public ExecutionPointer buildGenesisPointer(WorkflowDefinition def) { 14 | ExecutionPointer ep = new ExecutionPointer(); 15 | ep.id = generateId(); 16 | ep.active = true; 17 | ep.stepId = 0; 18 | ep.status = PointerStatus.Pending; 19 | 20 | return ep; 21 | } 22 | 23 | @Override 24 | public ExecutionPointer buildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId) { 25 | ExecutionPointer ep = new ExecutionPointer(); 26 | ep.id = generateId(); 27 | ep.predecessorId = exceptionPointer.id; 28 | ep.active = true; 29 | ep.stepId = compensationStepId; 30 | ep.status = PointerStatus.Pending; 31 | ep.contextItem = pointer.contextItem; 32 | ep.callStack = (Stack)pointer.callStack.clone(); 33 | 34 | return ep; 35 | } 36 | 37 | @Override 38 | public ExecutionPointer buildNextPointer(WorkflowDefinition def, ExecutionPointer pointer, StepOutcome outcomeTarget) { 39 | ExecutionPointer ep = new ExecutionPointer(); 40 | ep.id = generateId(); 41 | ep.predecessorId = pointer.id; 42 | ep.active = true; 43 | ep.stepId = outcomeTarget.getNextStep(); 44 | ep.status = PointerStatus.Pending; 45 | ep.contextItem = pointer.contextItem; 46 | ep.callStack = (Stack)pointer.callStack.clone(); 47 | 48 | return ep; 49 | } 50 | 51 | @Override 52 | public ExecutionPointer buildChildPointer(WorkflowDefinition def, ExecutionPointer pointer, int childDefinitionId, Object branch) { 53 | String childPointerId = generateId(); 54 | Stack childStack = (Stack)pointer.callStack.clone(); 55 | childStack.push(pointer.id); 56 | pointer.children.add(childPointerId); 57 | 58 | ExecutionPointer ep = new ExecutionPointer(); 59 | ep.id = childPointerId; 60 | ep.predecessorId = pointer.id; 61 | ep.active = true; 62 | ep.stepId = childDefinitionId; 63 | ep.status = PointerStatus.Pending; 64 | ep.contextItem = branch; 65 | ep.callStack = childStack; 66 | 67 | return ep; 68 | } 69 | 70 | private String generateId() { 71 | return UUID.randomUUID().toString(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/WhileScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.interfaces.Workflow; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | import net.jworkflow.kernel.models.WorkflowInstance; 8 | import net.jworkflow.kernel.models.WorkflowStatus; 9 | import org.junit.Test; 10 | import static org.junit.Assert.assertEquals; 11 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 12 | 13 | public class WhileScenario extends Scenario { 14 | 15 | private static int step1Ticker = 0; 16 | private static int step2Ticker = 0; 17 | private static int step3Ticker = 0; 18 | 19 | public class MyData { 20 | public int counter; 21 | } 22 | 23 | static class DoSomething implements StepBody { 24 | 25 | public int increment; 26 | 27 | @Override 28 | public ExecutionResult run(StepExecutionContext context) { 29 | step2Ticker++; 30 | increment = 1; 31 | return ExecutionResult.next(); 32 | } 33 | } 34 | 35 | class ScenarioWorkflow implements Workflow { 36 | 37 | @Override 38 | public String getId() { 39 | return "scenario"; 40 | } 41 | 42 | @Override 43 | public Class getDataType() { 44 | return Object.class; 45 | } 46 | 47 | @Override 48 | public int getVersion() { 49 | return 1; 50 | } 51 | 52 | @Override 53 | public void build(WorkflowBuilder builder) { 54 | builder 55 | .startsWith(context -> { 56 | step1Ticker++; 57 | return ExecutionResult.next(); 58 | }) 59 | .While(data -> data.counter < 3) 60 | .Do(each -> each 61 | .startsWith(DoSomething.class) 62 | .output((step, data) -> data.counter += step.increment) 63 | ) 64 | .then(context -> { 65 | step3Ticker++; 66 | return ExecutionResult.next(); 67 | }); 68 | } 69 | } 70 | 71 | @Test 72 | public void test() throws Exception { 73 | MyData data = new MyData(); 74 | data.counter = 0; 75 | WorkflowInstance result = runWorkflow(new ScenarioWorkflow(), data); 76 | 77 | assertEquals(WorkflowStatus.COMPLETE, result.getStatus()); 78 | assertEquals(1, step1Ticker); 79 | assertEquals(3, step2Ticker); 80 | assertEquals(1, step3Ticker); 81 | assertEquals(3, data.counter); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /jworkflow.providers.rabbitmq/src/main/java/net/jworkflow/providers/rabbitmq/RabbitMQQueueService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.rabbitmq; 2 | 3 | import com.rabbitmq.client.CancelCallback; 4 | import com.rabbitmq.client.Channel; 5 | import com.rabbitmq.client.Connection; 6 | import com.rabbitmq.client.ConnectionFactory; 7 | import com.rabbitmq.client.DeliverCallback; 8 | import com.rabbitmq.client.GetResponse; 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.concurrent.TimeoutException; 13 | import java.util.logging.Level; 14 | import java.util.logging.Logger; 15 | import net.jworkflow.kernel.interfaces.QueueService; 16 | import net.jworkflow.kernel.models.QueueType; 17 | 18 | public class RabbitMQQueueService implements QueueService { 19 | 20 | private final Connection connection; 21 | private final Logger logger; 22 | private final String queuePrefix = "jworkflow-"; 23 | private final Map queueNames; 24 | private final int waitTime = 5; 25 | 26 | public RabbitMQQueueService(ConnectionFactory connectionFactory) throws IOException, TimeoutException { 27 | this.logger = Logger.getLogger(getClass().getName()); 28 | connection = connectionFactory.newConnection(); 29 | queueNames = new HashMap<>(); 30 | queueNames.put(QueueType.WORKFLOW, createQueue(QueueType.WORKFLOW)); 31 | queueNames.put(QueueType.EVENT, createQueue(QueueType.EVENT)); 32 | } 33 | 34 | @Override 35 | public void queueForProcessing(QueueType type, String id) { 36 | try (Channel channel = connection.createChannel()) { 37 | channel.queueDeclare(queueNames.get(type), true, false, false, null); 38 | channel.basicPublish("", queueNames.get(type), null, id.getBytes()); 39 | } 40 | catch (Exception ex) { 41 | logger.log(Level.SEVERE, null, ex); 42 | } 43 | } 44 | 45 | @Override 46 | public String dequeueForProcessing(QueueType type) { 47 | try (Channel channel = connection.createChannel()) { 48 | channel.basicQos(1); 49 | channel.queueDeclare(queueNames.get(type), true, false, false, null); 50 | 51 | GetResponse msg = channel.basicGet(queueNames.get(type), true); 52 | if (msg == null) 53 | return null; 54 | 55 | return new String(msg.getBody(), "UTF-8"); 56 | } 57 | catch (Exception ex) { 58 | logger.log(Level.SEVERE, null, ex); 59 | return null; 60 | } 61 | } 62 | 63 | @Override 64 | public boolean isDequeueBlocking() { 65 | return false; 66 | } 67 | 68 | private String createQueue(QueueType type) throws IOException { 69 | String queueName = queuePrefix + type.toString(); 70 | return queueName; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/builders/DefaultStepOutcomeBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.builders; 2 | import net.jworkflow.kernel.interfaces.StepOutcomeBuilder; 3 | import net.jworkflow.kernel.models.*; 4 | import net.jworkflow.kernel.interfaces.StepBody; 5 | import java.util.function.Consumer; 6 | import java.util.function.Function; 7 | import net.jworkflow.kernel.interfaces.StepBuilder; 8 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 9 | 10 | 11 | public class DefaultStepOutcomeBuilder implements StepOutcomeBuilder { 12 | 13 | 14 | private final WorkflowBuilder workflowBuilder; 15 | private final StepOutcome outcome; 16 | private final Class dataClass; 17 | 18 | 19 | public DefaultStepOutcomeBuilder(Class dataClass, WorkflowBuilder workflowBuilder, StepOutcome outcome) { 20 | this.workflowBuilder = workflowBuilder; 21 | this.outcome = outcome; 22 | this.dataClass = dataClass; 23 | } 24 | 25 | 26 | @Override 27 | public StepBuilder then(Class stepClass) { 28 | return then(stepClass, x -> {}); 29 | } 30 | 31 | @Override 32 | public StepBuilder then(Class stepClass, Consumer> stepSetup) { 33 | WorkflowStep newStep = new WorkflowStep(stepClass); 34 | newStep.setName(stepClass.getName()); 35 | 36 | workflowBuilder.addStep(newStep); 37 | 38 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataClass, stepClass, workflowBuilder, newStep); 39 | 40 | if (stepSetup != null) 41 | stepSetup.accept(stepBuilder); 42 | 43 | outcome.setNextStep(newStep.getId()); 44 | 45 | return stepBuilder; 46 | } 47 | 48 | @Override 49 | public StepBuilder then(Class stepClass, StepBuilder newStep) { 50 | outcome.setNextStep(newStep.getStep().getId()); 51 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataClass, stepClass, workflowBuilder, newStep.getStep()); 52 | 53 | return stepBuilder; 54 | } 55 | 56 | @Override 57 | public StepBuilder then(Function body) { 58 | WorkflowStepInline newStep = new WorkflowStepInline(body); 59 | workflowBuilder.addStep(newStep); 60 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataClass, WorkflowStepInline.InlineBody.class, workflowBuilder, newStep); 61 | outcome.setNextStep(newStep.getId()); 62 | 63 | return stepBuilder; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/services/abstractions/WorkflowTest.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services.abstractions; 2 | 3 | import java.time.Instant; 4 | import java.time.temporal.TemporalUnit; 5 | import java.util.Collection; 6 | import java.util.Date; 7 | import net.jworkflow.kernel.interfaces.PersistenceService; 8 | import net.jworkflow.kernel.interfaces.Workflow; 9 | import net.jworkflow.kernel.interfaces.WorkflowHost; 10 | import net.jworkflow.kernel.models.EventSubscription; 11 | import net.jworkflow.kernel.models.WorkflowInstance; 12 | import net.jworkflow.kernel.models.WorkflowStatus; 13 | import net.jworkflow.WorkflowModule; 14 | 15 | public abstract class WorkflowTest { 16 | 17 | protected WorkflowHost host; 18 | protected PersistenceService persistence; 19 | 20 | //protected abstract WorkflowModule configure(); 21 | protected abstract Workflow getWorkflow(); 22 | 23 | protected void setup() throws Exception { 24 | WorkflowModule module = new WorkflowModule(); 25 | module.build(); 26 | host = module.getHost(); 27 | persistence = module.getPersistenceProvider(); 28 | host.registerWorkflow(getWorkflow()); 29 | host.start(); 30 | } 31 | 32 | protected String startWorkflow(Object data) throws Exception { 33 | return host.startWorkflow(getWorkflow().getId(), getWorkflow().getVersion(), data); 34 | } 35 | 36 | protected void waitForWorkflowToComplete(String workflowId) throws InterruptedException { 37 | WorkflowInstance instance = persistence.getWorkflowInstance(workflowId); 38 | int counter = 0; 39 | while ((instance.getStatus() == WorkflowStatus.RUNNABLE) && (counter < 100)) { 40 | Thread.sleep(100); 41 | instance = persistence.getWorkflowInstance(workflowId); 42 | counter++; 43 | } 44 | } 45 | 46 | protected void waitForEventSubscription(String eventName, String eventKey) throws InterruptedException { 47 | int counter = 0; 48 | while ((getActiveSubscriptons(eventName, eventKey).isEmpty()) && (counter < 100)) { 49 | Thread.sleep(100); 50 | counter++; 51 | } 52 | } 53 | 54 | protected WorkflowStatus getStatus(String workflowId) { 55 | WorkflowInstance instance = persistence.getWorkflowInstance(workflowId); 56 | return instance.getStatus(); 57 | } 58 | 59 | protected TData GetData(String workflowId) 60 | { 61 | WorkflowInstance instance = persistence.getWorkflowInstance(workflowId); 62 | return (TData)instance.getData(); 63 | } 64 | 65 | protected Collection getActiveSubscriptons(String eventName, String eventKey) { 66 | Date effectiveDate = Date.from(Instant.now()); 67 | return (Collection) persistence.getSubcriptions(eventName, eventKey, effectiveDate); 68 | } 69 | 70 | protected void teardown() { 71 | host.stop(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /jworkflow.providers.aws/src/main/java/net/jworkflow/providers/aws/SQSQueueService.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.providers.aws; 2 | 3 | import com.google.inject.Singleton; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.logging.Logger; 7 | import net.jworkflow.kernel.interfaces.QueueService; 8 | import net.jworkflow.kernel.models.QueueType; 9 | import software.amazon.awssdk.regions.Region; 10 | import software.amazon.awssdk.services.sqs.SqsClient; 11 | import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse; 12 | import software.amazon.awssdk.services.sqs.model.Message; 13 | import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse; 14 | import software.amazon.awssdk.services.sqs.model.SendMessageRequest; 15 | 16 | @Singleton 17 | public class SQSQueueService implements QueueService { 18 | 19 | private final SqsClient sqsClient; 20 | private final Logger logger; 21 | private final String queuePrefix = "jworkflow-"; 22 | private final Map queueUrls; 23 | private final int waitTime = 2; 24 | 25 | public SQSQueueService(Region region) { 26 | this.logger = Logger.getLogger(SQSQueueService.class.getName()); 27 | sqsClient = SqsClient.builder() 28 | .region(region) 29 | .build(); 30 | 31 | queueUrls = new HashMap<>(); 32 | queueUrls.put(QueueType.WORKFLOW, createQueue(QueueType.WORKFLOW)); 33 | queueUrls.put(QueueType.EVENT, createQueue(QueueType.EVENT)); 34 | } 35 | 36 | @Override 37 | public void queueForProcessing(QueueType type, String id) { 38 | SendMessageRequest request = SendMessageRequest.builder() 39 | .queueUrl(queueUrls.get(type)) 40 | .messageBody(id) 41 | .build(); 42 | 43 | sqsClient.sendMessage(request); 44 | } 45 | 46 | @Override 47 | public String dequeueForProcessing(QueueType type) { 48 | ReceiveMessageResponse response = sqsClient.receiveMessage(x -> x 49 | .maxNumberOfMessages(1) 50 | .waitTimeSeconds(waitTime) 51 | .queueUrl(queueUrls.get(type)) 52 | ); 53 | 54 | for (Message msg: response.messages()) { 55 | sqsClient.deleteMessage(x -> x 56 | .queueUrl(queueUrls.get(type)) 57 | .receiptHandle(msg.receiptHandle()) 58 | ); 59 | 60 | return msg.body(); 61 | } 62 | 63 | return null; 64 | } 65 | 66 | @Override 67 | public boolean isDequeueBlocking() { 68 | return true; 69 | } 70 | 71 | private String createQueue(QueueType type) { 72 | String queueName = queuePrefix + type.toString(); 73 | sqsClient.createQueue(builder -> builder.queueName(queueName)); 74 | 75 | GetQueueUrlResponse getQueueUrlResponse = sqsClient 76 | .getQueueUrl(builder -> builder.queueName(queueName)); 77 | return getQueueUrlResponse.queueUrl(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /samples/sample06/src/main/java/net/jworkflow/sample06/IfWorkflow.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.sample06; 2 | 3 | import net.jworkflow.sample06.steps.*; 4 | import net.jworkflow.kernel.interfaces.*; 5 | 6 | public class IfWorkflow implements Workflow { 7 | 8 | @Override 9 | public String getId() { 10 | return "if-sample"; 11 | } 12 | 13 | @Override 14 | public Class getDataType() { 15 | return MyData.class; 16 | } 17 | 18 | @Override 19 | public int getVersion() { 20 | return 1; 21 | } 22 | 23 | @Override 24 | public void build(WorkflowBuilder builder) { 25 | builder 26 | .startsWith(Hello.class) 27 | .If(data -> data.value1 > 2) 28 | .Do(then -> then 29 | .startsWith(PrintMessage.class) 30 | .input((step, data) -> step.message = "Value is greater than 2") 31 | .then(PrintMessage.class) 32 | .input((step, data) -> step.message = "Doing something...") 33 | ) 34 | .If(data -> data.value1 == 5) 35 | .Do(then -> then 36 | .startsWith(PrintMessage.class) 37 | .input((step, data) -> step.message = "Value is 5") 38 | .then(PrintMessage.class) 39 | .input((step, data) -> step.message = "Doing something...") 40 | ) 41 | .If(data -> data.value1 == 3) 42 | .Do(then -> then 43 | .startsWith(PrintMessage.class) 44 | .input((step, data) -> step.message = "Value is 3") 45 | .then(PrintMessage.class) 46 | .input((step, data) -> step.message = "Doing something...") 47 | ) 48 | .then(Goodbye.class); 49 | } 50 | } 51 | 52 | 53 | /* 54 | @Override 55 | public void build(WorkflowBuilder builder) { 56 | 57 | builder 58 | .startsWith(Hello.class) 59 | .then(MakeDecision.class) 60 | .when(0, then -> then 61 | .startsWith(PrintMessage.class) 62 | .input((step, data) -> step.message = "Doing something with my-outcome") 63 | .then(PrintMessage.class) 64 | .input((step, data) -> step.message = "Doing something else with my-outcome") 65 | ) 66 | .when(1, then -> then 67 | .startsWith(PrintMessage.class) 68 | .input((step, data) -> step.message = "Doing something with your-outcome") 69 | .then(PrintMessage.class) 70 | .input((step, data) -> step.message = "Doing something else with your-outcome") 71 | ) 72 | .then(Goodbye.class); 73 | } 74 | */ 75 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/PollThread.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | 3 | import net.jworkflow.kernel.interfaces.LockService; 4 | import net.jworkflow.kernel.interfaces.QueueService; 5 | import net.jworkflow.kernel.interfaces.PersistenceService; 6 | import com.google.inject.Inject; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.TimeUnit; 10 | import net.jworkflow.kernel.models.QueueType; 11 | import java.util.logging.Level; 12 | import java.util.logging.Logger; 13 | import net.jworkflow.kernel.interfaces.BackgroundService; 14 | 15 | public class PollThread implements BackgroundService { 16 | 17 | private final PersistenceService persistence; 18 | private final QueueService queueProvider; 19 | private final LockService lockProvider; 20 | private final Logger logger; 21 | private final ScheduledExecutorService scheduler; 22 | 23 | @Inject 24 | public PollThread(PersistenceService persistence, QueueService queueProvider, LockService lockProvider, Logger logger) { 25 | this.persistence = persistence; 26 | this.queueProvider = queueProvider; 27 | this.lockProvider = lockProvider; 28 | this.logger = logger; 29 | this.scheduler = Executors.newSingleThreadScheduledExecutor(); 30 | } 31 | 32 | public void run() { 33 | logger.log(Level.INFO, "Polling for runnables"); 34 | try { 35 | if (lockProvider.acquireLock("poll-workflows")) { 36 | try { 37 | Iterable runnables = persistence.getRunnableInstances(); 38 | runnables.forEach(item -> { 39 | queueProvider.queueForProcessing(QueueType.WORKFLOW, item); 40 | }); 41 | } 42 | finally { 43 | lockProvider.releaseLock("poll-workflows"); 44 | } 45 | } 46 | } 47 | catch (Exception ex) { 48 | logger.log(Level.SEVERE, ex.getMessage()); 49 | } 50 | 51 | try { 52 | if (lockProvider.acquireLock("poll-events")) { 53 | try { 54 | Iterable runnables = persistence.getRunnableEvents(); 55 | runnables.forEach(item -> { 56 | queueProvider.queueForProcessing(QueueType.EVENT, item); 57 | }); 58 | } 59 | finally { 60 | lockProvider.releaseLock("poll-events"); 61 | } 62 | } 63 | } 64 | catch (Exception ex) { 65 | logger.log(Level.SEVERE, ex.getMessage()); 66 | } 67 | } 68 | 69 | @Override 70 | public void start() { 71 | scheduler.scheduleAtFixedRate(() -> run(), 10, 10, TimeUnit.SECONDS); 72 | } 73 | 74 | @Override 75 | public void stop() { 76 | scheduler.shutdownNow(); 77 | } 78 | } -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/SagaScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.interfaces.Workflow; 5 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 6 | import net.jworkflow.kernel.models.ExecutionResult; 7 | import net.jworkflow.kernel.models.StepExecutionContext; 8 | import net.jworkflow.kernel.services.abstractions.WorkflowTest; 9 | import org.junit.Test; 10 | import static org.junit.Assert.*; 11 | 12 | public class SagaScenario extends WorkflowTest { 13 | 14 | private static int beginTicker = 0; 15 | private static int endTicker = 0; 16 | 17 | private static int step1Ticker = 0; 18 | private static int step2Ticker = 0; 19 | private static int step3Ticker = 0; 20 | 21 | private static int undoStep1Ticker = 0; 22 | private static int undoStep2Ticker = 0; 23 | private static int undoStep3Ticker = 0; 24 | 25 | static class ExplodingStep implements StepBody { 26 | 27 | @Override 28 | public ExecutionResult run(StepExecutionContext context) throws Exception { 29 | step2Ticker++; 30 | throw new Exception(); 31 | } 32 | } 33 | 34 | class EventWorkflow implements Workflow { 35 | 36 | @Override 37 | public String getId() { 38 | return "scenario"; 39 | } 40 | 41 | @Override 42 | public Class getDataType() { 43 | return Object.class; 44 | } 45 | 46 | @Override 47 | public int getVersion() { 48 | return 1; 49 | } 50 | 51 | @Override 52 | public void build(WorkflowBuilder builder) { 53 | builder 54 | .startsWithAction(context -> beginTicker++) 55 | .saga(saga -> saga 56 | .startsWithAction(context -> step1Ticker++) 57 | .compensateWithAction(context -> undoStep1Ticker++) 58 | .then(ExplodingStep.class) 59 | .compensateWithAction(context -> undoStep2Ticker++) 60 | .thenAction(context -> step3Ticker++) 61 | .compensateWithAction(context -> undoStep3Ticker++) 62 | ) 63 | .thenAction(context -> endTicker++); 64 | } 65 | } 66 | 67 | @Override 68 | protected Workflow getWorkflow() { 69 | return new EventWorkflow(); 70 | } 71 | 72 | @Test 73 | public void Scenario() throws Exception { 74 | setup(); 75 | 76 | String workflowId = startWorkflow(null); 77 | waitForWorkflowToComplete(workflowId); 78 | 79 | assertEquals(1, beginTicker); 80 | assertEquals(1, step1Ticker); 81 | assertEquals(1, step2Ticker); 82 | assertEquals(0, step3Ticker); 83 | assertEquals(1, undoStep1Ticker); 84 | assertEquals(1, undoStep2Ticker); 85 | assertEquals(0, undoStep3Ticker); 86 | assertEquals(1, endTicker); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/test/java/net/jworkflow/kernel/scenarios/DataIOScenario.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.scenarios; 2 | 3 | import net.jworkflow.kernel.interfaces.StepBody; 4 | import net.jworkflow.kernel.interfaces.Workflow; 5 | import net.jworkflow.kernel.models.ExecutionResult; 6 | import net.jworkflow.kernel.models.StepExecutionContext; 7 | import net.jworkflow.kernel.models.WorkflowInstance; 8 | import net.jworkflow.kernel.models.WorkflowStatus; 9 | import org.junit.Test; 10 | import static org.junit.Assert.assertEquals; 11 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 12 | 13 | public class DataIOScenario extends Scenario{ 14 | 15 | private static int step1Ticker = 0; 16 | private static int step2Ticker = 0; 17 | private static int finalResult = 0; 18 | 19 | public class MyData { 20 | public int value1; 21 | public int value2; 22 | public int value3; 23 | } 24 | 25 | static class AddNumbers implements StepBody { 26 | 27 | public int number1; 28 | public int number2; 29 | public int answer; 30 | 31 | @Override 32 | public ExecutionResult run(StepExecutionContext context) { 33 | step1Ticker++; 34 | answer = number1 + number2; 35 | return ExecutionResult.next(); 36 | } 37 | } 38 | 39 | static class GetResult implements StepBody { 40 | 41 | public int result; 42 | 43 | @Override 44 | public ExecutionResult run(StepExecutionContext context) { 45 | step2Ticker++; 46 | finalResult = result; 47 | return ExecutionResult.next(); 48 | } 49 | } 50 | 51 | class BasicWorkflow implements Workflow { 52 | 53 | @Override 54 | public String getId() { 55 | return "scenario"; 56 | } 57 | 58 | @Override 59 | public Class getDataType() { 60 | return Object.class; 61 | } 62 | 63 | @Override 64 | public int getVersion() { 65 | return 1; 66 | } 67 | 68 | @Override 69 | public void build(WorkflowBuilder builder) { 70 | builder 71 | .startsWith(AddNumbers.class) 72 | .input((step, data) -> step.number1 = data.value1) 73 | .input((step, data) -> step.number2 = data.value2) 74 | .output((step, data) -> data.value3 = step.answer) 75 | .then(GetResult.class) 76 | .input((step, data) -> step.result = data.value3); 77 | } 78 | } 79 | 80 | @Test 81 | public void test() throws Exception { 82 | MyData data = new MyData(); 83 | data.value1 = 2; 84 | data.value2 = 3; 85 | WorkflowInstance result = runWorkflow(new BasicWorkflow(), data); 86 | 87 | assertEquals(WorkflowStatus.COMPLETE, result.getStatus()); 88 | assertEquals(1, step1Ticker); 89 | assertEquals(1, step2Ticker); 90 | assertEquals(5, finalResult); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/models/WorkflowInstance.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.models; 2 | 3 | import java.io.Serializable; 4 | import java.util.Collection; 5 | import java.util.Date; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Queue; 9 | 10 | public class WorkflowInstance implements Serializable { 11 | 12 | private String id; 13 | private String workflowDefintionId; 14 | private int version; 15 | private String description; 16 | private Long nextExecution; 17 | private WorkflowStatus status; 18 | private Object data; 19 | private Date createTimeUtc; 20 | private Date completeTimeUtc; 21 | private ExecutionPointerCollection executionPointers; 22 | 23 | public WorkflowInstance() { 24 | this.executionPointers = new ExecutionPointerCollection(); 25 | 26 | } 27 | 28 | 29 | public String getId() { 30 | return id; 31 | } 32 | 33 | public void setId(String value) { 34 | this.id = value; 35 | } 36 | 37 | public String getWorkflowDefintionId() { 38 | return workflowDefintionId; 39 | } 40 | 41 | public void setWorkflowDefintionId(String workflowDefintionId) { 42 | this.workflowDefintionId = workflowDefintionId; 43 | } 44 | 45 | public int getVersion() { 46 | return version; 47 | } 48 | 49 | public void setVersion(int version) { 50 | this.version = version; 51 | } 52 | 53 | public String getDescription() { 54 | return description; 55 | } 56 | 57 | public void setDescription(String description) { 58 | this.description = description; 59 | } 60 | 61 | public Long getNextExecution() { 62 | return nextExecution; 63 | } 64 | 65 | public void setNextExecution(Long nextExecution) { 66 | this.nextExecution = nextExecution; 67 | } 68 | 69 | public WorkflowStatus getStatus() { 70 | return status; 71 | } 72 | 73 | public void setStatus(WorkflowStatus status) { 74 | this.status = status; 75 | } 76 | 77 | public Object getData() { 78 | return data; 79 | } 80 | 81 | public void setData(Object data) { 82 | this.data = data; 83 | } 84 | 85 | public Date getCreateTimeUtc() { 86 | return createTimeUtc; 87 | } 88 | 89 | public void setCreateTimeUtc(Date createTime) { 90 | this.createTimeUtc = createTime; 91 | } 92 | 93 | public Date getCompleteTimeUtc() { 94 | return completeTimeUtc; 95 | } 96 | 97 | public void setCompleteTimeUtc(Date completeTime) { 98 | this.completeTimeUtc = completeTime; 99 | } 100 | 101 | public ExecutionPointerCollection getExecutionPointers() { 102 | return executionPointers; 103 | } 104 | 105 | public void setExecutionPointers(List pointers) { 106 | this.executionPointers = new ExecutionPointerCollection(pointers); 107 | } 108 | 109 | public boolean isBranchComplete(String parentId) { 110 | return executionPointers 111 | .findByStackFrame(parentId) 112 | .stream() 113 | .allMatch(x -> x.endTimeUtc != null); 114 | } 115 | } -------------------------------------------------------------------------------- /docs/05-control-structures.md: -------------------------------------------------------------------------------- 1 | ## Control Structures 2 | 3 | ### If condition 4 | 5 | ```java 6 | @Override 7 | public void build(WorkflowBuilder builder) { 8 | builder 9 | .startsWith(Hello.class) 10 | .If(data -> data.value1 > 2) 11 | .Do(then -> then 12 | .startsWith(PrintMessage.class) 13 | .input((step, data) -> step.message = "Value is greater than 2") 14 | .then(PrintMessage.class) 15 | .input((step, data) -> step.message = "Doing something...") 16 | ) 17 | .If(data -> data.value1 == 5) 18 | .Do(then -> then 19 | .startsWith(PrintMessage.class) 20 | .input((step, data) -> step.message = "Value is 5") 21 | .then(PrintMessage.class) 22 | .input((step, data) -> step.message = "Doing something...") 23 | ) 24 | .If(data -> data.value1 == 3) 25 | .Do(then -> then 26 | .startsWith(PrintMessage.class) 27 | .input((step, data) -> step.message = "Value is 3") 28 | .then(PrintMessage.class) 29 | .input((step, data) -> step.message = "Doing something...") 30 | ) 31 | .then(Goodbye.class); 32 | } 33 | ``` 34 | 35 | ### While loop 36 | 37 | ```java 38 | @Override 39 | public void build(WorkflowBuilder builder) { 40 | builder 41 | .startsWith(Hello.class) 42 | .While(data -> data.value1 < 3) 43 | .Do(each -> each 44 | .startsWith(IncrementValue.class) 45 | .input((step, data) -> step.value = data.value1) 46 | .output((step, data) -> data.value1 = step.value) 47 | ) 48 | .then(Goodbye.class); 49 | } 50 | ``` 51 | ### Parallel ForEach 52 | 53 | ```java 54 | @Override 55 | public void build(WorkflowBuilder builder) { 56 | 57 | builder 58 | .startsWith(Hello.class) 59 | .foreach(data -> data.value1) //either values from workflow data 60 | .Do(each -> each 61 | .startsWith(DoSomething.class)) 62 | .then(Hello.class) 63 | .foreach(data -> new String[] { "item 1", "item 2", "item 3" }) //or values defined inline 64 | .Do(each -> each 65 | .startsWith(DoSomething.class)) 66 | .then(Goodbye.class); 67 | } 68 | ``` 69 | 70 | #### Parallel Paths 71 | 72 | Use the .parallel() method to branch parallel tasks 73 | 74 | ```java 75 | @Override 76 | public void build(WorkflowBuilder builder) { 77 | 78 | builder 79 | .startsWith(Hello.class) 80 | .parallel() 81 | .Do(p1 -> p1 82 | .startsWith(DoSomething.class) 83 | .then(DoSomethingElse.class); 84 | ) 85 | .Do(p2 -> p2 86 | .startsWith(DoSomethingAmazing.class) 87 | .then(DoSomething.class); 88 | ) 89 | .Do(p3 -> p3 90 | .startsWith(DoSomethingDodgy.class) 91 | .then(DoSomethingElse.class); 92 | ) 93 | .then(Goodbye.class); 94 | } 95 | ``` -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/WorkflowWorker.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | 3 | import net.jworkflow.kernel.interfaces.WorkflowExecutor; 4 | import net.jworkflow.kernel.interfaces.LockService; 5 | import net.jworkflow.kernel.interfaces.QueueService; 6 | import com.google.inject.Inject; 7 | import net.jworkflow.kernel.models.QueueType; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | import net.jworkflow.kernel.interfaces.PersistenceService; 11 | import net.jworkflow.kernel.models.EventSubscription; 12 | import net.jworkflow.kernel.models.WorkflowExecutorResult; 13 | import net.jworkflow.kernel.models.WorkflowInstance; 14 | 15 | public class WorkflowWorker extends QueueWorker { 16 | 17 | private final WorkflowExecutor executor; 18 | private final PersistenceService persistenceStore; 19 | private final LockService lockProvider; 20 | 21 | @Inject 22 | public WorkflowWorker(WorkflowExecutor executor, PersistenceService persistence, QueueService queueProvider, LockService lockProvider, Logger logger) { 23 | super(queueProvider, logger); 24 | this.executor = executor; 25 | this.lockProvider = lockProvider; 26 | this.persistenceStore = persistence; 27 | } 28 | 29 | @Override 30 | protected QueueType getQueueType() { 31 | return QueueType.WORKFLOW; 32 | } 33 | 34 | @Override 35 | protected void executeItem(String item) throws Exception { 36 | if (!lockProvider.acquireLock(item)) { 37 | logger.log(Level.INFO, String.format("Workflow %s locked", item)); 38 | return; 39 | } 40 | 41 | WorkflowExecutorResult result = new WorkflowExecutorResult(); 42 | try { 43 | WorkflowInstance workflow = persistenceStore.getWorkflowInstance(item); 44 | try { 45 | result = executor.execute(workflow); 46 | } 47 | finally { 48 | persistenceStore.persistWorkflow(workflow); 49 | } 50 | } 51 | finally { 52 | lockProvider.releaseLock(item); 53 | 54 | for (EventSubscription evt: result.subscriptions) { 55 | subscribeEvent(evt); 56 | } 57 | 58 | if (result.requeue) { 59 | logger.log(Level.INFO, String.format("Requeue workflow %s", item)); 60 | queueProvider.queueForProcessing(QueueType.WORKFLOW, item); 61 | } 62 | } 63 | } 64 | 65 | private void subscribeEvent(EventSubscription subscription) { 66 | //TODO: move to own class 67 | logger.log(Level.INFO, String.format("Subscribing to event {%s} {%s} for workflow {%s} step {%s}", subscription.eventName, subscription.eventKey, subscription.workflowId, subscription.stepId)); 68 | 69 | persistenceStore.createEventSubscription(subscription); 70 | 71 | Iterable events = persistenceStore.getEvents(subscription.eventName, subscription.eventKey, subscription.subscribeAsOfUtc); 72 | for (String evt: events) { 73 | persistenceStore.markEventUnprocessed(evt); 74 | queueProvider.queueForProcessing(QueueType.EVENT, evt); 75 | } 76 | } 77 | 78 | @Override 79 | protected int getThreadCount() { 80 | return Runtime.getRuntime().availableProcessors(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/services/DefaultWorkflowRegistry.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.services; 2 | import net.jworkflow.kernel.builders.BaseWorkflowBuilder; 3 | import net.jworkflow.kernel.models.WorkflowDefinition; 4 | import net.jworkflow.kernel.interfaces.WorkflowRegistry; 5 | import net.jworkflow.kernel.interfaces.Workflow; 6 | import com.google.inject.Inject; 7 | import com.google.inject.Singleton; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.Hashtable; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Objects; 14 | import java.util.function.BiConsumer; 15 | import java.util.logging.Level; 16 | import java.util.logging.Logger; 17 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 18 | 19 | @Singleton 20 | public class DefaultWorkflowRegistry implements WorkflowRegistry{ 21 | 22 | class RegistryEntry { 23 | private final String id; 24 | private final int version; 25 | private final WorkflowDefinition definition; 26 | 27 | public RegistryEntry(String id, int version, WorkflowDefinition definition) { 28 | this.id = id; 29 | this.version = version; 30 | this.definition = definition; 31 | } 32 | 33 | public String getId() { 34 | return id; 35 | } 36 | 37 | public int getVersion() { 38 | return version; 39 | } 40 | 41 | public WorkflowDefinition getDefinition() { 42 | return definition; 43 | } 44 | 45 | 46 | } 47 | 48 | 49 | private final List registry; 50 | private final Logger logger; 51 | 52 | @Inject 53 | public DefaultWorkflowRegistry(Logger logger) { 54 | this.registry = new ArrayList<>(); 55 | this.logger = logger; 56 | } 57 | 58 | @Override 59 | public void registerWorkflow(Workflow workflow) throws Exception { 60 | 61 | //if (registry.containsKey(key)) 62 | // throw new Exception("already registered"); 63 | 64 | BaseWorkflowBuilder baseBuilder = new BaseWorkflowBuilder(); 65 | WorkflowBuilder builder = baseBuilder.UseData(workflow.getDataType()); 66 | 67 | workflow.build(builder); 68 | WorkflowDefinition def = builder.build(workflow.getId(), workflow.getVersion()); 69 | 70 | RegistryEntry entry = new RegistryEntry(workflow.getId(), workflow.getVersion(), def); 71 | 72 | registry.add(entry); 73 | 74 | logger.log(Level.INFO, String.format("Registered workflow %s %s", workflow.getId(), workflow.getVersion())); 75 | } 76 | 77 | @Override 78 | public void registerWorkflow(WorkflowDefinition definition) throws Exception { 79 | RegistryEntry entry = new RegistryEntry(definition.getId(), definition.getVersion(), definition); 80 | registry.add(entry); 81 | logger.log(Level.INFO, String.format("Registered workflow %s %s", definition.getId(), definition.getVersion())); 82 | } 83 | 84 | @Override 85 | public WorkflowDefinition getDefinition(String workflowId, int version) { 86 | for (RegistryEntry item : registry) { 87 | if (item.getId().equals(workflowId) && (item.version == version)) 88 | return item.getDefinition(); 89 | } 90 | 91 | return null; 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /jworkflow.kernel/src/main/java/net/jworkflow/kernel/builders/DefaultWorkflowBuilder.java: -------------------------------------------------------------------------------- 1 | package net.jworkflow.kernel.builders; 2 | 3 | import java.util.Comparator; 4 | import net.jworkflow.kernel.models.WorkflowDefinition; 5 | import net.jworkflow.kernel.models.WorkflowStep; 6 | import net.jworkflow.kernel.interfaces.StepBody; 7 | import java.util.List; 8 | import java.util.NoSuchElementException; 9 | import java.util.function.Consumer; 10 | import net.jworkflow.kernel.interfaces.StepBuilder; 11 | import net.jworkflow.kernel.interfaces.StepExecutionConsumer; 12 | import net.jworkflow.kernel.interfaces.StepFieldConsumer; 13 | import net.jworkflow.kernel.interfaces.WorkflowBuilder; 14 | import net.jworkflow.kernel.models.StepExecutionContext; 15 | import net.jworkflow.kernel.models.WorkflowStepInline; 16 | import net.jworkflow.primitives.ConsumerStep; 17 | 18 | public class DefaultWorkflowBuilder extends BaseWorkflowBuilder implements WorkflowBuilder { 19 | 20 | private final Class dataType; 21 | 22 | public DefaultWorkflowBuilder(Class dataType, List steps) { 23 | super(); 24 | this.steps = steps; 25 | this.dataType = dataType; 26 | } 27 | 28 | @Override 29 | public WorkflowDefinition build(String id, int version) { 30 | WorkflowDefinition result = super.build(id, version); 31 | result.setDataType(dataType); 32 | return result; 33 | } 34 | 35 | @Override 36 | public StepBuilder startsWith(Class stepClass) { 37 | return startsWith(stepClass, null); 38 | } 39 | 40 | @Override 41 | public StepBuilder startsWith(Class stepClass, Consumer> stepSetup) { 42 | WorkflowStep step = new WorkflowStep(stepClass); 43 | step.setName(stepClass.getName()); 44 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataType, stepClass, this, step); 45 | 46 | if (stepSetup != null) 47 | stepSetup.accept(stepBuilder); 48 | 49 | addStep(step); 50 | 51 | return stepBuilder; 52 | } 53 | 54 | @Override 55 | public int getLastStep() { 56 | return steps.stream() 57 | .max(Comparator.comparing(WorkflowStep::getId)) 58 | .orElseThrow(NoSuchElementException::new) 59 | .getId(); 60 | } 61 | 62 | @Override 63 | public StepBuilder startsWith(StepExecutionConsumer body) { 64 | WorkflowStepInline step = new WorkflowStepInline(body); 65 | addStep(step); 66 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataType, WorkflowStepInline.InlineBody.class, this, step); 67 | 68 | return stepBuilder; 69 | } 70 | 71 | @Override 72 | public StepBuilder startsWithAction(Consumer body) { 73 | WorkflowStep newStep = new WorkflowStep(ConsumerStep.class); 74 | StepFieldConsumer bodyConsumer = (step, data) -> step.body = body; 75 | newStep.addInput(bodyConsumer); 76 | StepBuilder stepBuilder = new DefaultStepBuilder<>(dataType, ConsumerStep.class, this, newStep); 77 | addStep(newStep); 78 | 79 | return stepBuilder; 80 | } 81 | } --------------------------------------------------------------------------------