Skip to content

Commit ebe180e

Browse files
committed
spring boot workflow patterns initial version
Signed-off-by: salaboy <[email protected]>
1 parent daf4c8b commit ebe180e

24 files changed

+1051
-0
lines changed

spring-boot-examples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<modules>
2222
<module>producer-app</module>
2323
<module>consumer-app</module>
24+
<module>workflows</module>
2425
</modules>
2526

2627
<dependencyManagement>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>io.dapr</groupId>
8+
<artifactId>spring-boot-examples</artifactId>
9+
<version>0.16.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>workflows</artifactId>
13+
<name>workflows</name>
14+
<description>Spring Boot, Testcontainers and Dapr Integration Examples :: Workflows</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-actuator</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>org.springframework.boot</groupId>
23+
<artifactId>spring-boot-starter-web</artifactId>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-test</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>io.dapr.spring</groupId>
31+
<artifactId>dapr-spring-boot-starter</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>io.dapr.spring</groupId>
35+
<artifactId>dapr-spring-boot-starter-test</artifactId>
36+
<scope>test</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>io.rest-assured</groupId>
40+
<artifactId>rest-assured</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
</dependencies>
44+
45+
<build>
46+
<plugins>
47+
<plugin>
48+
<groupId>org.springframework.boot</groupId>
49+
<artifactId>spring-boot-maven-plugin</artifactId>
50+
</plugin>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-site-plugin</artifactId>
54+
<configuration>
55+
<skip>true</skip>
56+
</configuration>
57+
</plugin>
58+
<plugin>
59+
<groupId>org.apache.maven.plugins</groupId>
60+
<artifactId>maven-checkstyle-plugin</artifactId>
61+
<configuration>
62+
<!-- Skip checkstyle for auto-generated code -->
63+
<skip>true</skip>
64+
</configuration>
65+
</plugin>
66+
</plugins>
67+
</build>
68+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp;
15+
16+
import org.springframework.boot.SpringApplication;
17+
import org.springframework.boot.autoconfigure.SpringBootApplication;
18+
19+
20+
@SpringBootApplication
21+
public class WorkflowPatternsApplication {
22+
23+
public static void main(String[] args) {
24+
SpringApplication.run(WorkflowPatternsApplication.class, args);
25+
}
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp;
15+
16+
import io.dapr.spring.workflows.config.EnableDaprWorkflows;
17+
import io.dapr.springboot.examples.wfp.chain.ChainWorkflow;
18+
import io.dapr.springboot.examples.wfp.child.ParentWorkflow;
19+
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
20+
import io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow;
21+
import io.dapr.springboot.examples.wfp.externalevent.Decision;
22+
import io.dapr.springboot.examples.wfp.externalevent.ExternalEventWorkflow;
23+
import io.dapr.springboot.examples.wfp.fanoutin.FanOutInWorkflow;
24+
import io.dapr.springboot.examples.wfp.fanoutin.Result;
25+
import io.dapr.workflows.client.DaprWorkflowClient;
26+
import io.dapr.workflows.client.WorkflowInstanceStatus;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
import org.springframework.beans.factory.annotation.Autowired;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.web.bind.annotation.PostMapping;
32+
import org.springframework.web.bind.annotation.RequestBody;
33+
import org.springframework.web.bind.annotation.RequestParam;
34+
import org.springframework.web.bind.annotation.RestController;
35+
36+
import java.time.Duration;
37+
import java.util.List;
38+
import java.util.concurrent.TimeoutException;
39+
40+
@RestController
41+
@EnableDaprWorkflows
42+
public class WorkflowPatternsRestController {
43+
44+
private final Logger logger = LoggerFactory.getLogger(WorkflowPatternsRestController.class);
45+
46+
@Autowired
47+
private DaprWorkflowClient daprWorkflowClient;
48+
49+
@Bean
50+
public CleanUpLog cleanUpLog(){
51+
return new CleanUpLog();
52+
}
53+
54+
/**
55+
* Run Chain Demo Workflow
56+
* @return the output of the ChainWorkflow execution
57+
*/
58+
@PostMapping("wfp/chain")
59+
public String chain() throws TimeoutException {
60+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ChainWorkflow.class);
61+
logger.info("Workflow instance " + instanceId + " started");
62+
return daprWorkflowClient
63+
.waitForInstanceCompletion(instanceId, Duration.ofSeconds(2), true)
64+
.readOutputAs(String.class);
65+
}
66+
67+
68+
/**
69+
* Run Child Demo Workflow
70+
* @return confirmation that the workflow instance was created for the workflow pattern child
71+
*/
72+
@PostMapping("wfp/child")
73+
public String child() throws TimeoutException {
74+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ParentWorkflow.class);
75+
logger.info("Workflow instance " + instanceId + " started");
76+
return daprWorkflowClient
77+
.waitForInstanceCompletion(instanceId, Duration.ofSeconds(2), true)
78+
.readOutputAs(String.class);
79+
}
80+
81+
82+
/**
83+
* Run Fan Out/in Demo Workflow
84+
* @return confirmation that the workflow instance was created for the workflow pattern faninout
85+
*/
86+
@PostMapping("wfp/fanoutin")
87+
public Result faninout(@RequestBody List<String> listOfStrings) throws TimeoutException {
88+
89+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(FanOutInWorkflow.class, listOfStrings);
90+
logger.info("Workflow instance " + instanceId + " started");
91+
92+
// Block until the orchestration completes. Then print the final status, which includes the output.
93+
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient.waitForInstanceCompletion(
94+
instanceId,
95+
Duration.ofSeconds(30),
96+
true);
97+
logger.info("workflow instance with ID: %s completed with result: %s%n", instanceId,
98+
workflowInstanceStatus.readOutputAs(Result.class));
99+
return workflowInstanceStatus.readOutputAs(Result.class);
100+
}
101+
102+
/**
103+
* Run External Event Workflow Pattern
104+
* @return confirmation that the workflow instance was created for the workflow pattern externalevent
105+
*/
106+
@PostMapping("wfp/externalevent")
107+
public String externalevent() {
108+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ExternalEventWorkflow.class);
109+
logger.info("Workflow instance " + instanceId + " started");
110+
return instanceId;
111+
}
112+
113+
@PostMapping("wfp/externalevent-continue")
114+
public Decision externaleventContinue(@RequestParam("instanceId") String instanceId, @RequestParam("decision") Boolean decision)
115+
throws TimeoutException {
116+
logger.info("Workflow instance " + instanceId + " continue");
117+
daprWorkflowClient.raiseEvent(instanceId, "Approval", decision);
118+
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient
119+
.waitForInstanceCompletion(instanceId, null, true);
120+
return workflowInstanceStatus.readOutputAs(Decision.class);
121+
}
122+
123+
@PostMapping("wfp/continueasnew")
124+
public CleanUpLog continueasnew()
125+
throws TimeoutException {
126+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ContinueAsNewWorkflow.class);
127+
logger.info("Workflow instance " + instanceId + " started");
128+
129+
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient.waitForInstanceCompletion(instanceId, null, true);
130+
System.out.printf("workflow instance with ID: %s completed.", instanceId);
131+
return workflowInstanceStatus.readOutputAs(CleanUpLog.class);
132+
}
133+
134+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.chain;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.springframework.stereotype.Component;
19+
20+
@Component
21+
public class ChainWorkflow implements Workflow {
22+
@Override
23+
public WorkflowStub create() {
24+
return ctx -> {
25+
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
26+
27+
String result = "";
28+
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "Tokyo", String.class).await() + ", ";
29+
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "London", String.class).await() + ", ";
30+
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "Seattle", String.class).await();
31+
32+
ctx.getLogger().info("Workflow finished with result: " + result);
33+
ctx.complete(result);
34+
};
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.chain;
15+
16+
import io.dapr.workflows.WorkflowActivity;
17+
import io.dapr.workflows.WorkflowActivityContext;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
import org.springframework.stereotype.Component;
21+
22+
@Component
23+
public class ToUpperCaseActivity implements WorkflowActivity {
24+
25+
@Override
26+
public Object run(WorkflowActivityContext ctx) {
27+
Logger logger = LoggerFactory.getLogger(ToUpperCaseActivity.class);
28+
logger.info("Starting Activity: " + ctx.getName());
29+
30+
var message = ctx.getInput(String.class);
31+
var newMessage = message.toUpperCase();
32+
33+
logger.info("Message Received from input: " + message);
34+
logger.info("Sending message to output: " + newMessage);
35+
36+
return newMessage;
37+
}
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.child;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.springframework.stereotype.Component;
19+
20+
@Component
21+
public class ChildWorkflow implements Workflow {
22+
@Override
23+
public WorkflowStub create() {
24+
return ctx -> {
25+
ctx.getLogger().info("Starting ChildWorkflow: " + ctx.getName());
26+
27+
var childWorkflowInput = ctx.getInput(String.class);
28+
ctx.getLogger().info("ChildWorkflow received input: " + childWorkflowInput);
29+
30+
ctx.getLogger().info("ChildWorkflow is calling Activity: " + ReverseActivity.class.getName());
31+
String result = ctx.callActivity(ReverseActivity.class.getName(), childWorkflowInput, String.class).await();
32+
33+
ctx.getLogger().info("ChildWorkflow finished with: " + result);
34+
ctx.complete(result);
35+
};
36+
}
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2023 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.wfp.child;
15+
16+
import io.dapr.workflows.Workflow;
17+
import io.dapr.workflows.WorkflowStub;
18+
import org.springframework.stereotype.Component;
19+
20+
@Component
21+
public class ParentWorkflow implements Workflow {
22+
@Override
23+
public WorkflowStub create() {
24+
return ctx -> {
25+
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
26+
27+
var childWorkflowInput = "Hello Dapr Workflow!";
28+
ctx.getLogger().info("calling childworkflow with input: " + childWorkflowInput);
29+
30+
var childWorkflowOutput =
31+
ctx.callChildWorkflow(ChildWorkflow.class.getName(), childWorkflowInput, String.class).await();
32+
33+
ctx.getLogger().info("childworkflow finished with: " + childWorkflowOutput);
34+
ctx.complete(childWorkflowOutput);
35+
};
36+
}
37+
}

0 commit comments

Comments
 (0)