Skip to content

Commit 86bb3b3

Browse files
authored
Merge branch 'master' into actor_ttl
2 parents d60805f + bd3a54d commit 86bb3b3

File tree

34 files changed

+1519
-629
lines changed

34 files changed

+1519
-629
lines changed

dapr-spring/dapr-spring-workflows/src/main/java/io/dapr/spring/workflows/config/DaprWorkflowsConfiguration.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
public class DaprWorkflowsConfiguration implements ApplicationContextAware {
1818
private static final Logger LOGGER = LoggerFactory.getLogger(DaprWorkflowsConfiguration.class);
1919

20-
private WorkflowRuntimeBuilder workflowRuntimeBuilder;
20+
private final WorkflowRuntimeBuilder workflowRuntimeBuilder;
2121

2222
public DaprWorkflowsConfiguration(WorkflowRuntimeBuilder workflowRuntimeBuilder) {
2323
this.workflowRuntimeBuilder = workflowRuntimeBuilder;
@@ -29,16 +29,21 @@ public DaprWorkflowsConfiguration(WorkflowRuntimeBuilder workflowRuntimeBuilder)
2929
*/
3030
private void registerWorkflowsAndActivities(ApplicationContext applicationContext) {
3131
LOGGER.info("Registering Dapr Workflows and Activities");
32+
3233
Map<String, Workflow> workflowBeans = applicationContext.getBeansOfType(Workflow.class);
33-
for (Workflow w : workflowBeans.values()) {
34-
LOGGER.info("Dapr Workflow: '{}' registered", w.getClass().getName());
35-
workflowRuntimeBuilder.registerWorkflow(w.getClass());
34+
35+
for (Workflow workflow : workflowBeans.values()) {
36+
LOGGER.info("Dapr Workflow: '{}' registered", workflow.getClass().getName());
37+
38+
workflowRuntimeBuilder.registerWorkflow(workflow);
3639
}
3740

3841
Map<String, WorkflowActivity> workflowActivitiesBeans = applicationContext.getBeansOfType(WorkflowActivity.class);
39-
for (WorkflowActivity a : workflowActivitiesBeans.values()) {
40-
LOGGER.info("Dapr Workflow Activity: '{}' registered", a.getClass().getName());
41-
workflowRuntimeBuilder.registerActivity(a.getClass());
42+
43+
for (WorkflowActivity activity : workflowActivitiesBeans.values()) {
44+
LOGGER.info("Dapr Workflow Activity: '{}' registered", activity.getClass().getName());
45+
46+
workflowRuntimeBuilder.registerActivity(activity);
4247
}
4348

4449
try (WorkflowRuntime runtime = workflowRuntimeBuilder.build()) {

daprdocs/content/en/java-sdk-docs/spring-boot/_index.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Besides the previous configuration (`DaprTestContainersConfig`) your tests shoul
122122
The Java SDK allows you to interface with all of the [Dapr building blocks]({{< ref building-blocks >}}).
123123
But if you want to leverage the Spring and Spring Boot programming model you can use the `dapr-spring-boot-starter` integration.
124124
This includes implementations of Spring Data (`KeyValueTemplate` and `CrudRepository`) as well as a `DaprMessagingTemplate` for producing and consuming messages
125-
(similar to [Spring Kafka](https://spring.io/projects/spring-kafka), [Spring Pulsar](https://spring.io/projects/spring-pulsar) and [Spring AMQP for RabbitMQ](https://spring.io/projects/spring-amqp)).
125+
(similar to [Spring Kafka](https://spring.io/projects/spring-kafka), [Spring Pulsar](https://spring.io/projects/spring-pulsar) and [Spring AMQP for RabbitMQ](https://spring.io/projects/spring-amqp)) and Dapr workflows.
126126

127127
## Using Spring Data `CrudRepository` and `KeyValueTemplate`
128128

@@ -277,6 +277,53 @@ public static void setup(){
277277

278278
You can check and run the [full example source code here](https://github.com/salaboy/dapr-spring-boot-docs-examples).
279279

280+
## Using Dapr Workflows with Spring Boot
281+
282+
Following the same approach that we used for Spring Data and Spring Messaging, the `dapr-spring-boot-starter` brings Dapr Workflow integration for Spring Boot users.
283+
284+
To work with Dapr Workflows you need to define and implement your workflows using code. The Dapr Spring Boot Starter makes your life easier by managing `Workflow`s and `WorkflowActivity`s as Spring beans.
285+
286+
In order to enable the automatic bean discovery you can annotate your `@SpringBootApplication` with the `@EnableDaprWorkflows` annotation:
287+
288+
```
289+
@SpringBootApplication
290+
@EnableDaprWorkflows
291+
public class MySpringBootApplication {}
292+
```
293+
294+
By adding this annotation, all the `WorkflowActivity`s will be automatically managed by Spring and registered to the workflow engine.
295+
296+
By having all `WorkflowActivity`s as managed beans we can use Spring `@Autowired` mechanism to inject any bean that our workflow activity might need to implement its functionality, for example the `@RestTemplate`:
297+
298+
```
299+
public class MyWorkflowActivity implements WorkflowActivity {
300+
301+
@Autowired
302+
private RestTemplate restTemplate;
303+
```
304+
305+
You can also `@Autowired` the `DaprWorkflowClient` to create new instances of your workflows.
306+
307+
```
308+
@Autowired
309+
private DaprWorkflowClient daprWorkflowClient;
310+
```
311+
312+
This enable applications to schedule new workflow instances and raise events.
313+
314+
```
315+
String instanceId = daprWorkflowClient.scheduleNewWorkflow(MyWorkflow.class, payload);
316+
```
317+
318+
and
319+
320+
```
321+
daprWorkflowClient.raiseEvent(instanceId, "MyEvenet", event);
322+
```
323+
324+
Check the [Dapr Workflow documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) for more information about how to work with Dapr Workflows.
325+
326+
280327
## Next steps
281328

282329
Learn more about the [Dapr Java SDK packages available to add to your Java applications](https://dapr.github.io/java-sdk/).

examples/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@
134134
<artifactId>protobuf-java</artifactId>
135135
<version>${protobuf.version}</version>
136136
</dependency>
137+
<dependency>
138+
<groupId>com.squareup.okhttp3</groupId>
139+
<artifactId>okhttp</artifactId>
140+
<version>4.12.0</version>
141+
</dependency>
137142
</dependencies>
138143

139144
<build>

examples/src/main/java/io/dapr/examples/OpenTelemetryInterceptor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
import jakarta.servlet.DispatcherType;
2020
import jakarta.servlet.http.HttpServletRequest;
2121
import jakarta.servlet.http.HttpServletResponse;
22-
import org.jetbrains.annotations.Nullable;
2322
import org.springframework.beans.factory.annotation.Autowired;
2423
import org.springframework.stereotype.Component;
2524
import org.springframework.web.servlet.HandlerInterceptor;
2625
import org.springframework.web.servlet.ModelAndView;
2726

27+
import javax.annotation.Nullable;
28+
2829
import java.util.Collections;
2930

3031
@Component

sdk-actors/src/test/java/io/dapr/client/DaprHttpProxy.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

sdk-tests/src/test/java/io/dapr/it/resiliency/WaitForSidecarIT.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
import java.time.Duration;
2323

24+
import static org.assertj.core.api.Assertions.assertThat;
2425
import static org.junit.jupiter.api.Assertions.assertThrows;
25-
import static org.junit.jupiter.api.Assertions.assertTrue;
2626

2727
/**
2828
* Test SDK resiliency.
@@ -43,7 +43,7 @@ public class WaitForSidecarIT extends BaseIT {
4343
@BeforeAll
4444
public static void init() throws Exception {
4545
daprRun = startDaprApp(WaitForSidecarIT.class.getSimpleName(), 5000);
46-
daprNotRunning = startDaprApp(WaitForSidecarIT.class.getSimpleName()+"NotRunning", 5000);
46+
daprNotRunning = startDaprApp(WaitForSidecarIT.class.getSimpleName() + "NotRunning", 5000);
4747
daprNotRunning.stop();
4848

4949
toxiProxyRun = new ToxiProxyRun(daprRun, LATENCY, JITTER);
@@ -61,24 +61,30 @@ public void waitSucceeds() throws Exception {
6161
public void waitTimeout() {
6262
int timeoutInMillis = (int)LATENCY.minusMillis(100).toMillis();
6363
long started = System.currentTimeMillis();
64+
6465
assertThrows(RuntimeException.class, () -> {
6566
try(var client = toxiProxyRun.newDaprClientBuilder().build()) {
6667
client.waitForSidecar(timeoutInMillis).block();
6768
}
6869
});
70+
6971
long duration = System.currentTimeMillis() - started;
70-
assertTrue(duration >= timeoutInMillis);
72+
73+
assertThat(duration).isGreaterThanOrEqualTo(timeoutInMillis);
7174
}
7275

7376
@Test
7477
public void waitSlow() throws Exception {
7578
int timeoutInMillis = (int)LATENCY.plusMillis(100).toMillis();
7679
long started = System.currentTimeMillis();
80+
7781
try(var client = toxiProxyRun.newDaprClientBuilder().build()) {
7882
client.waitForSidecar(timeoutInMillis).block();
7983
}
84+
8085
long duration = System.currentTimeMillis() - started;
81-
assertTrue(duration >= LATENCY.toMillis());
86+
87+
assertThat(duration).isGreaterThanOrEqualTo(LATENCY.toMillis());
8288
}
8389

8490
@Test
@@ -87,12 +93,15 @@ public void waitNotRunningTimeout() {
8793
// This has to do with a previous bug in the implementation.
8894
int timeoutMilliseconds = 5000;
8995
long started = System.currentTimeMillis();
96+
9097
assertThrows(RuntimeException.class, () -> {
9198
try(var client = daprNotRunning.newDaprClientBuilder().build()) {
9299
client.waitForSidecar(timeoutMilliseconds).block();
93100
}
94101
});
102+
95103
long duration = System.currentTimeMillis() - started;
96-
assertTrue(duration >= timeoutMilliseconds);
104+
105+
assertThat(duration).isGreaterThanOrEqualTo(timeoutMilliseconds);
97106
}
98107
}

sdk-tests/src/test/java/io/dapr/it/spring/messaging/DaprSpringMessagingIT.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,14 @@
2323
import org.junit.jupiter.api.BeforeEach;
2424
import org.junit.jupiter.api.Tag;
2525
import org.junit.jupiter.api.Test;
26-
import org.junit.jupiter.api.Disabled;
2726
import org.slf4j.Logger;
2827
import org.slf4j.LoggerFactory;
2928
import org.springframework.beans.factory.annotation.Autowired;
3029
import org.springframework.boot.test.context.SpringBootTest;
3130
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
3231
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
33-
import org.springframework.test.context.DynamicPropertyRegistry;
34-
import org.springframework.test.context.DynamicPropertySource;
3532
import org.testcontainers.containers.Network;
33+
import org.testcontainers.containers.wait.strategy.Wait;
3634
import org.testcontainers.junit.jupiter.Container;
3735
import org.testcontainers.junit.jupiter.Testcontainers;
3836

@@ -56,16 +54,18 @@ public class DaprSpringMessagingIT {
5654
private static final Logger logger = LoggerFactory.getLogger(DaprSpringMessagingIT.class);
5755

5856
private static final String TOPIC = "mockTopic";
59-
6057
private static final Network DAPR_NETWORK = Network.newNetwork();
58+
private static final int APP_PORT = 8080;
59+
private static final String SUBSCRIPTION_MESSAGE_PATTERN = ".*app is subscribed to the following topics.*";
6160

6261
@Container
6362
@ServiceConnection
6463
private static final DaprContainer DAPR_CONTAINER = new DaprContainer("daprio/daprd:1.13.2")
6564
.withAppName("messaging-dapr-app")
6665
.withNetwork(DAPR_NETWORK)
6766
.withComponent(new Component("pubsub", "pubsub.in-memory", "v1", Collections.emptyMap()))
68-
.withAppPort(8080)
67+
.withAppPort(APP_PORT)
68+
.withAppHealthCheckPath("/ready")
6969
.withDaprLogLevel(DaprLogLevel.DEBUG)
7070
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
7171
.withAppChannelAddress("host.testcontainers.internal");
@@ -78,16 +78,16 @@ public class DaprSpringMessagingIT {
7878

7979
@BeforeAll
8080
public static void beforeAll(){
81-
org.testcontainers.Testcontainers.exposeHostPorts(8080);
81+
org.testcontainers.Testcontainers.exposeHostPorts(APP_PORT);
8282
}
8383

8484
@BeforeEach
85-
public void beforeEach() throws InterruptedException {
86-
Thread.sleep(1000);
85+
public void beforeEach() {
86+
// Ensure the subscriptions are registered
87+
Wait.forLogMessage(SUBSCRIPTION_MESSAGE_PATTERN, 1).waitUntilReady(DAPR_CONTAINER);
8788
}
8889

8990
@Test
90-
@Disabled("Test is flaky due to global state in the spring test application.")
9191
public void testDaprMessagingTemplate() throws InterruptedException {
9292
for (int i = 0; i < 10; i++) {
9393
var msg = "ProduceAndReadWithPrimitiveMessageType:" + i;

sdk-tests/src/test/java/io/dapr/it/spring/messaging/TestRestController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class TestRestController {
3333
private static final Logger LOG = LoggerFactory.getLogger(TestRestController.class);
3434
private final List<CloudEvent<String>> events = new ArrayList<>();
3535

36-
@GetMapping("/")
36+
@GetMapping("/ready")
3737
public String ok() {
3838
return "OK";
3939
}

sdk-workflows/src/main/java/io/dapr/workflows/runtime/WorkflowActivityWrapper.java renamed to sdk-workflows/src/main/java/io/dapr/workflows/runtime/WorkflowActivityClassWrapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
/**
2424
* Wrapper for Durable Task Framework task activity factory.
2525
*/
26-
public class WorkflowActivityWrapper<T extends WorkflowActivity> implements TaskActivityFactory {
26+
public class WorkflowActivityClassWrapper<T extends WorkflowActivity> implements TaskActivityFactory {
2727
private final Constructor<T> activityConstructor;
2828
private final String name;
2929

@@ -32,7 +32,7 @@ public class WorkflowActivityWrapper<T extends WorkflowActivity> implements Task
3232
*
3333
* @param clazz Class of the activity to wrap.
3434
*/
35-
public WorkflowActivityWrapper(Class<T> clazz) {
35+
public WorkflowActivityClassWrapper(Class<T> clazz) {
3636
this.name = clazz.getCanonicalName();
3737
try {
3838
this.activityConstructor = clazz.getDeclaredConstructor();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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.workflows.runtime;
15+
16+
import com.microsoft.durabletask.TaskActivity;
17+
import com.microsoft.durabletask.TaskActivityFactory;
18+
import io.dapr.workflows.WorkflowActivity;
19+
20+
/**
21+
* Wrapper for Durable Task Framework task activity factory.
22+
*/
23+
public class WorkflowActivityInstanceWrapper<T extends WorkflowActivity> implements TaskActivityFactory {
24+
private final T activity;
25+
private final String name;
26+
27+
/**
28+
* Constructor for WorkflowActivityWrapper.
29+
*
30+
* @param instance Instance of the activity to wrap.
31+
*/
32+
public WorkflowActivityInstanceWrapper(T instance) {
33+
this.name = instance.getClass().getCanonicalName();
34+
this.activity = instance;
35+
}
36+
37+
@Override
38+
public String getName() {
39+
return name;
40+
}
41+
42+
@Override
43+
public TaskActivity create() {
44+
return ctx -> activity.run(new DefaultWorkflowActivityContext(ctx));
45+
}
46+
}

0 commit comments

Comments
 (0)