mirror of https://github.com/dapr/java-sdk.git
Compare commits
32 Commits
v1.15.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
7cf6533afd | |
|
2454fec841 | |
|
e3fc48d4eb | |
|
981b3b457b | |
|
dcaca773b3 | |
|
e13f934efe | |
|
3a8fd611da | |
|
7e2f81d0e3 | |
|
13a973bf5a | |
|
447e2bfa86 | |
|
114e354363 | |
|
1852cc5590 | |
|
c466d1b743 | |
|
e6f7c6eb06 | |
|
6cecd76745 | |
|
ba3b529830 | |
|
949584f69f | |
|
a99d286a88 | |
|
8ea5ea4f4b | |
|
7291d4c74d | |
|
1e4bcf9b9f | |
|
c3592b446d | |
|
e84d2c4e61 | |
|
e4cc0303fa | |
|
c07e07b782 | |
|
a1ec3ce898 | |
|
910b13ba56 | |
|
824f357f58 | |
|
daf4c8b703 | |
|
551d205b1d | |
|
4e1fbbe2ce | |
|
ab8e41111d |
|
@ -121,7 +121,7 @@ jobs:
|
|||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v5.4.2
|
||||
uses: codecov/codecov-action@v5.4.3
|
||||
- name: Install jars
|
||||
run: ./mvnw install -q -B -DskipTests
|
||||
- name: Integration tests using spring boot version ${{ matrix.spring-boot-version }}
|
||||
|
|
|
@ -35,12 +35,12 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
|
||||
- name: "Run FOSSA Scan"
|
||||
uses: fossas/fossa-action@v1.6.0 # Use a specific version if locking is preferred
|
||||
uses: fossas/fossa-action@v1.7.0 # Use a specific version if locking is preferred
|
||||
with:
|
||||
api-key: ${{ env.FOSSA_API_KEY }}
|
||||
|
||||
- name: "Run FOSSA Test"
|
||||
uses: fossas/fossa-action@v1.6.0 # Use a specific version if locking is preferred
|
||||
uses: fossas/fossa-action@v1.7.0 # Use a specific version if locking is preferred
|
||||
with:
|
||||
api-key: ${{ env.FOSSA_API_KEY }}
|
||||
run-tests: true
|
||||
|
|
|
@ -216,4 +216,10 @@ jobs:
|
|||
run: |
|
||||
mm.py README.md
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
- name: Validate Spring Boot Workflow examples
|
||||
working-directory: ./spring-boot-examples/workflows
|
||||
run: |
|
||||
mm.py README.md
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
|
|
|
@ -54,7 +54,48 @@ This section describes the guidelines for contributing code / docs to Dapr.
|
|||
### Things to consider when adding new API to SDK
|
||||
|
||||
1. All the new API's go under [dapr-sdk maven package](https://github.com/dapr/java-sdk/tree/master/sdk)
|
||||
2. Make sure there is an example talking about how to use the API along with a README. [Example](https://github.com/dapr/java-sdk/pull/1235/files#diff-69ed756c4c01fd5fa884aac030dccb8f3f4d4fefa0dc330862d55a6f87b34a14)
|
||||
2. Make sure there is an example talking about how to use the API along with a README with mechanical markdown. [Example](https://github.com/dapr/java-sdk/pull/1235/files#diff-69ed756c4c01fd5fa884aac030dccb8f3f4d4fefa0dc330862d55a6f87b34a14)
|
||||
|
||||
#### Mechanical Markdown
|
||||
|
||||
Mechanical markdown is used to validate example outputs in our CI pipeline. It ensures that the expected output in README files matches the actual output when running the examples. This helps maintain example output, catches any unintended changes in example behavior, and regressions.
|
||||
|
||||
To test mechanical markdown locally:
|
||||
|
||||
1. Install the package:
|
||||
```bash
|
||||
pip3 install mechanical-markdown
|
||||
```
|
||||
|
||||
2. Run the test from the respective examples README directory, for example:
|
||||
```bash
|
||||
cd examples
|
||||
mm.py ./src/main/java/io/dapr/examples/workflows/README.md
|
||||
```
|
||||
|
||||
The test will:
|
||||
- Parse the STEP markers in the README
|
||||
- Execute the commands specified in the markers
|
||||
- Compare the actual output with the expected output
|
||||
- Report any mismatches
|
||||
|
||||
When writing STEP markers:
|
||||
- Use `output_match_mode: substring` for flexible matching
|
||||
- Quote strings containing special YAML characters (like `:`, `*`, `'`)
|
||||
- Set appropriate timeouts for long-running examples
|
||||
|
||||
Example STEP marker:
|
||||
```yaml
|
||||
<!-- STEP
|
||||
name: Run example
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- "Starting workflow: io.dapr.examples.workflows.compensation.BookTripWorkflow"
|
||||
...
|
||||
background: true
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
```
|
||||
|
||||
### Pull Requests
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
<module name="ModifierOrder"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
<property name="allowMultipleEmptyLines" value="false"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapDot"/>
|
||||
|
@ -287,4 +288,13 @@
|
|||
<module name="SuppressWarningsHolder"/>
|
||||
</module>
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="${checkstyle.header.file}"/>
|
||||
<property name="fileExtensions" value="java,xml"/>
|
||||
</module>
|
||||
<module name="SuppressionSingleFilter">
|
||||
<property name="checks" value="RegexpHeader"/>
|
||||
<property name="files" value=".*\.properties$"/>
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
</module>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-spring-boot-autoconfigure</artifactId>
|
||||
|
@ -18,19 +18,20 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-data</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-messaging</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-workflows</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -71,9 +72,20 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>testcontainers-dapr</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -13,11 +13,11 @@ limitations under the License.
|
|||
|
||||
package io.dapr.spring.boot.autoconfigure.client;
|
||||
|
||||
class PropertiesDaprConnectionDetails implements DaprConnectionDetails {
|
||||
public class ClientPropertiesDaprConnectionDetails implements DaprConnectionDetails {
|
||||
|
||||
private final DaprClientProperties daprClientProperties;
|
||||
|
||||
public PropertiesDaprConnectionDetails(DaprClientProperties daprClientProperties) {
|
||||
public ClientPropertiesDaprConnectionDetails(DaprClientProperties daprClientProperties) {
|
||||
this.daprClientProperties = daprClientProperties;
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ public class DaprClientAutoConfiguration {
|
|||
@Bean
|
||||
@ConditionalOnMissingBean(DaprConnectionDetails.class)
|
||||
DaprConnectionDetails daprConnectionDetails(DaprClientProperties properties) {
|
||||
return new PropertiesDaprConnectionDetails(properties);
|
||||
return new ClientPropertiesDaprConnectionDetails(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -23,7 +23,6 @@ public class DaprClientProperties {
|
|||
private Integer httpPort;
|
||||
private Integer grpcPort;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a {@link DaprClientProperties}.
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,7 @@ package io.dapr.spring.boot.autoconfigure.client;
|
|||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
public interface DaprConnectionDetails extends ConnectionDetails {
|
||||
|
||||
String getHttpEndpoint();
|
||||
|
||||
String getGrpcEndpoint();
|
||||
|
@ -23,4 +24,5 @@ public interface DaprConnectionDetails extends ConnectionDetails {
|
|||
Integer getHttpPort();
|
||||
|
||||
Integer getGrpcPort();
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ package io.dapr.spring.boot.autoconfigure.pubsub;
|
|||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
|
||||
@ConfigurationProperties(prefix = DaprPubSubProperties.CONFIG_PREFIX)
|
||||
public class DaprPubSubProperties {
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -23,12 +23,10 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-tests</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>testcontainers-dapr</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -23,27 +23,22 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-springboot</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-data</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-messaging</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-workflows</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-spring-boot-tests</artifactId>
|
||||
|
@ -22,7 +22,6 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
|
@ -38,7 +37,6 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>testcontainers-dapr</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.spring.boot.testcontainers.service.connection;
|
||||
|
||||
import io.dapr.spring.boot.autoconfigure.client.DaprConnectionDetails;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-spring-data</artifactId>
|
||||
|
@ -19,6 +19,10 @@
|
|||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-keyvalue</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -76,7 +76,6 @@ public class MySQLDaprKeyValueAdapter extends AbstractDaprKeyValueAdapter {
|
|||
this.bindingName = bindingName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> getAllOf(String keyspace, Class<T> type) {
|
||||
Assert.hasText(keyspace, "Keyspace must not be empty");
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.spring.data.repository.query;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
|
@ -18,7 +31,6 @@ public class DaprPredicate implements Predicate<Object> {
|
|||
this(path, expected, (valueToCompare) -> ObjectUtils.nullSafeEquals(valueToCompare, expected));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DaprPredicate}.
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-spring-messaging</artifactId>
|
||||
|
@ -14,6 +14,17 @@
|
|||
<description>Dapr Spring Messaging</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -192,7 +192,6 @@ public class DaprMessagingTemplate<T> implements DaprMessagingOperations<T>, App
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void send() {
|
||||
template.doSend(topic, message);
|
||||
|
@ -202,7 +201,5 @@ public class DaprMessagingTemplate<T> implements DaprMessagingOperations<T>, App
|
|||
public Mono<Void> sendAsync() {
|
||||
return template.doSendAsync(topic, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ public final class DaprMessagingSenderContext extends SenderContext<DaprMessagin
|
|||
return carrier.properties();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The name of the bean sending the message (typically a {@code DaprMessagingTemplate}).
|
||||
* @return the name of the bean sending the message
|
||||
|
@ -75,7 +74,6 @@ public final class DaprMessagingSenderContext extends SenderContext<DaprMessagin
|
|||
return this.destination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Acts as a carrier for a Dapr message and records the propagated properties for
|
||||
* later access by the Dapr.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-spring-workflows</artifactId>
|
||||
|
@ -15,10 +15,17 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-workflows</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.spring.workflows.config;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
package io.dapr.spring.workflows.config;
|
||||
/*
|
||||
* Copyright 2021 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.spring.workflows.config;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
<name>dapr-spring-parent</name>
|
||||
<description>SDK extension for Spring and Spring Boot</description>
|
||||
|
||||
|
@ -28,12 +28,15 @@
|
|||
</modules>
|
||||
|
||||
<properties>
|
||||
<springboot.version>3.2.6</springboot.version>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.release>11</maven.compiler.release>
|
||||
<testcontainers.version>1.19.8</testcontainers.version>
|
||||
<junit.version>5.10.2</junit.version>
|
||||
<junit.version>5.11.2</junit.version>
|
||||
<dapr.spring.version>0.16.0-SNAPSHOT</dapr.spring.version>
|
||||
<springboot.version>3.2.6</springboot.version>
|
||||
<springframework.version>6.1.20</springframework.version>
|
||||
<logback-core.version>1.4.14</logback-core.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -43,76 +46,130 @@
|
|||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-workflows</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-data</artifactId>
|
||||
<version>${dapr.spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-messaging</artifactId>
|
||||
<version>${dapr.spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-workflows</artifactId>
|
||||
<version>${dapr.spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-autoconfigure</artifactId>
|
||||
<version>${dapr.spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-springboot</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${springframework.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${springframework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-tests</artifactId>
|
||||
<version>${dapr.spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback-core.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Dapr dependencies -->
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: docs
|
||||
title: "AI"
|
||||
linkTitle: "AI"
|
||||
weight: 3000
|
||||
description: With the Dapr Conversation AI package, you can interact with the Dapr AI workloads from a Java application. To get started, walk through the [Dapr AI]({{< ref java-ai-howto.md >}}) how-to guide.
|
||||
---
|
|
@ -0,0 +1,105 @@
|
|||
---
|
||||
type: docs
|
||||
title: "How to: Author and manage Dapr Conversation AI in the Java SDK"
|
||||
linkTitle: "How to: Author and manage Conversation AI"
|
||||
weight: 20000
|
||||
description: How to get up and running with Conversation AI using the Dapr Java SDK
|
||||
---
|
||||
|
||||
As part of this demonstration, we will look at how to use the Conversation API to converse with a Large Language Model (LLM). The API
|
||||
will return the response from the LLM for the given prompt. With the [provided conversation ai example](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/conversation), you will:
|
||||
|
||||
- You will provide a prompt using the [Conversation AI example](https://github.com/dapr/java-sdk/blob/master/examples/src/main/java/io/dapr/examples/conversation/DemoConversationAI.java)
|
||||
- Filter out Personally identifiable information (PII).
|
||||
|
||||
This example uses the default configuration from `dapr init` in [self-hosted mode](https://github.com/dapr/cli#install-dapr-on-your-local-machine-self-hosted).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
||||
- Java JDK 11 (or greater):
|
||||
- [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or
|
||||
- OpenJDK
|
||||
- [Apache Maven](https://maven.apache.org/install.html), version 3.x.
|
||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||
|
||||
## Set up the environment
|
||||
|
||||
Clone the [Java SDK repo](https://github.com/dapr/java-sdk) and navigate into it.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/dapr/java-sdk.git
|
||||
cd java-sdk
|
||||
```
|
||||
|
||||
Run the following command to install the requirements for running the Conversation AI example with the Dapr Java SDK.
|
||||
|
||||
```bash
|
||||
mvn clean install -DskipTests
|
||||
```
|
||||
|
||||
From the Java SDK root directory, navigate to the examples' directory.
|
||||
|
||||
```bash
|
||||
cd examples
|
||||
```
|
||||
|
||||
Run the Dapr sidecar.
|
||||
|
||||
```sh
|
||||
dapr run --app-id conversationapp --dapr-grpc-port 51439 --dapr-http-port 3500 --app-port 8080
|
||||
```
|
||||
|
||||
> Now, Dapr is listening for HTTP requests at `http://localhost:3500` and gRPC requests at `http://localhost:51439`.
|
||||
|
||||
## Send a prompt with Personally identifiable information (PII) to the Conversation AI API
|
||||
|
||||
In the `DemoConversationAI` there are steps to send a prompt using the `converse` method under the `DaprPreviewClient`.
|
||||
|
||||
```java
|
||||
public class DemoConversationAI {
|
||||
/**
|
||||
* The main method to start the client.
|
||||
*
|
||||
* @param args Input arguments (unused).
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
try (DaprPreviewClient client = new DaprClientBuilder().buildPreviewClient()) {
|
||||
System.out.println("Sending the following input to LLM: Hello How are you? This is the my number 672-123-4567");
|
||||
|
||||
ConversationInput daprConversationInput = new ConversationInput("Hello How are you? "
|
||||
+ "This is the my number 672-123-4567");
|
||||
|
||||
// Component name is the name provided in the metadata block of the conversation.yaml file.
|
||||
Mono<ConversationResponse> responseMono = client.converse(new ConversationRequest("echo",
|
||||
List.of(daprConversationInput))
|
||||
.setContextId("contextId")
|
||||
.setScrubPii(true).setTemperature(1.1d));
|
||||
ConversationResponse response = responseMono.block();
|
||||
System.out.printf("Conversation output: %s", response.getConversationOutputs().get(0).getResult());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run the `DemoConversationAI` with the following command.
|
||||
|
||||
```sh
|
||||
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.conversation.DemoConversationAI
|
||||
```
|
||||
|
||||
### Sample output
|
||||
```
|
||||
== APP == Conversation output: Hello How are you? This is the my number <ISBN>
|
||||
```
|
||||
|
||||
As shown in the output, the number sent to the API is obfuscated and returned in the form of <ISBN>.
|
||||
The example above uses an ["echo"](https://docs.dapr.io/developing-applications/building-blocks/conversation/howto-conversation-layer/#set-up-the-conversation-component)
|
||||
component for testing, which simply returns the input message.
|
||||
When integrated with LLMs like OpenAI or Claude, you’ll receive meaningful responses instead of echoed input.
|
||||
|
||||
## Next steps
|
||||
- [Learn more about Conversation AI]({{< ref conversation-overview.md >}})
|
||||
- [Conversation AI API reference]({{< ref conversation_api.md >}})
|
|
@ -24,21 +24,7 @@ You can initialize a Dapr client as so:
|
|||
DaprClient client = new DaprClientBuilder().build()
|
||||
```
|
||||
|
||||
This will connect to the default Dapr gRPC endpoint `localhost:50001`.
|
||||
|
||||
|
||||
#### Environment variables:
|
||||
|
||||
##### Dapr Sidecar Endpoints
|
||||
You can use the standardized `DAPR_GRPC_ENDPOINT` and `DAPR_HTTP_ENDPOINT` environment variables to
|
||||
specify a different gRPC or HTTP endpoint. When these variables are set, the client will automatically use them to connect to the Dapr sidecar.
|
||||
|
||||
The legacy environment variables `DAPR_HTTP_PORT` and `DAPR_GRPC_PORT` are still supported, but `DAPR_GRPC_ENDPOINT` and `DAPR_HTTP_ENDPOINT` take precedence.
|
||||
|
||||
##### Dapr API Token
|
||||
If your Dapr instance is configured to require the `DAPR_API_TOKEN` environment variable, you can
|
||||
set it in the environment and the client will use it automatically.
|
||||
You can read more about Dapr API token authentication [here](https://docs.dapr.io/operations/security/api-token/).
|
||||
This will connect to the default Dapr gRPC endpoint `localhost:50001`. For information about configuring the client using environment variables and system properties, see [Properties]({{< ref properties.md >}}).
|
||||
|
||||
#### Error Handling
|
||||
|
||||
|
@ -648,3 +634,5 @@ Learn more about the [Dapr Java SDK packages available to add to your Java appli
|
|||
|
||||
## Related links
|
||||
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)
|
||||
|
||||
For a full list of SDK properties and how to configure them, visit [Properties]({{< ref properties.md >}}).
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
---
|
||||
type: docs
|
||||
title: "Properties"
|
||||
linkTitle: "Properties"
|
||||
weight: 3001
|
||||
description: SDK-wide properties for configuring the Dapr Java SDK using environment variables and system properties
|
||||
---
|
||||
|
||||
# Properties
|
||||
|
||||
The Dapr Java SDK provides a set of global properties that control the behavior of the SDK. These properties can be configured using environment variables or system properties. System properties can be set using the `-D` flag when running your Java application.
|
||||
|
||||
These properties affect the entire SDK, including clients and runtime. They control aspects such as:
|
||||
- Sidecar connectivity (endpoints, ports)
|
||||
- Security settings (TLS, API tokens)
|
||||
- Performance tuning (timeouts, connection pools)
|
||||
- Protocol settings (gRPC, HTTP)
|
||||
- String encoding
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The following environment variables are available for configuring the Dapr Java SDK:
|
||||
|
||||
### Sidecar Endpoints
|
||||
|
||||
When these variables are set, the client will automatically use them to connect to the Dapr sidecar.
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_GRPC_ENDPOINT` | The gRPC endpoint for the Dapr sidecar | `localhost:50001` |
|
||||
| `DAPR_HTTP_ENDPOINT` | The HTTP endpoint for the Dapr sidecar | `localhost:3500` |
|
||||
| `DAPR_GRPC_PORT` | The gRPC port for the Dapr sidecar (legacy, `DAPR_GRPC_ENDPOINT` takes precedence) | `50001` |
|
||||
| `DAPR_HTTP_PORT` | The HTTP port for the Dapr sidecar (legacy, `DAPR_HTTP_ENDPOINT` takes precedence) | `3500` |
|
||||
|
||||
### API Token
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_API_TOKEN` | API token for authentication between app and Dapr sidecar. This is the same token used by the Dapr runtime for API authentication. For more details, see [Dapr API token authentication](https://docs.dapr.io/operations/security/api-token/) and [Environment variables reference](https://docs.dapr.io/reference/environment/#dapr_api_token). | `null` |
|
||||
|
||||
### gRPC Configuration
|
||||
|
||||
#### TLS Settings
|
||||
For secure gRPC communication, you can configure TLS settings using the following environment variables:
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_GRPC_TLS_INSECURE` | When set to "true", enables insecure TLS mode which still uses TLS but doesn't verify certificates. This uses InsecureTrustManagerFactory to trust all certificates. This should only be used for testing or in secure environments. | `false` |
|
||||
| `DAPR_GRPC_TLS_CA_PATH` | Path to the CA certificate file. This is used for TLS connections to servers with self-signed certificates. | `null` |
|
||||
| `DAPR_GRPC_TLS_CERT_PATH` | Path to the TLS certificate file for client authentication. | `null` |
|
||||
| `DAPR_GRPC_TLS_KEY_PATH` | Path to the TLS private key file for client authentication. | `null` |
|
||||
|
||||
#### Keepalive Settings
|
||||
Configure gRPC keepalive behavior using these environment variables:
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_GRPC_ENABLE_KEEP_ALIVE` | Whether to enable gRPC keepalive | `false` |
|
||||
| `DAPR_GRPC_KEEP_ALIVE_TIME_SECONDS` | gRPC keepalive time in seconds | `10` |
|
||||
| `DAPR_GRPC_KEEP_ALIVE_TIMEOUT_SECONDS` | gRPC keepalive timeout in seconds | `5` |
|
||||
| `DAPR_GRPC_KEEP_ALIVE_WITHOUT_CALLS` | Whether to keep gRPC connection alive without calls | `true` |
|
||||
|
||||
### HTTP Client Configuration
|
||||
|
||||
These properties control the behavior of the HTTP client used for communication with the Dapr sidecar:
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_HTTP_CLIENT_READ_TIMEOUT_SECONDS` | Timeout in seconds for HTTP client read operations. This is the maximum time to wait for a response from the Dapr sidecar. | `60` |
|
||||
| `DAPR_HTTP_CLIENT_MAX_REQUESTS` | Maximum number of concurrent HTTP requests that can be executed. Above this limit, requests will queue in memory waiting for running calls to complete. | `1024` |
|
||||
| `DAPR_HTTP_CLIENT_MAX_IDLE_CONNECTIONS` | Maximum number of idle connections in the HTTP connection pool. This is the maximum number of connections that can remain idle in the pool. | `128` |
|
||||
|
||||
### API Configuration
|
||||
|
||||
These properties control the behavior of API calls made through the SDK:
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_API_MAX_RETRIES` | Maximum number of retries for retriable exceptions when making API calls to the Dapr sidecar | `0` |
|
||||
| `DAPR_API_TIMEOUT_MILLISECONDS` | Timeout in milliseconds for API calls to the Dapr sidecar. A value of 0 means no timeout. | `0` |
|
||||
|
||||
### String Encoding
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|---------------------|-------------|---------|
|
||||
| `DAPR_STRING_CHARSET` | Character set used for string encoding/decoding in the SDK. Must be a valid Java charset name. | `UTF-8` |
|
||||
|
||||
### System Properties
|
||||
|
||||
All environment variables can be set as system properties using the `-D` flag. Here is the complete list of available system properties:
|
||||
|
||||
| System Property | Description | Default |
|
||||
|----------------|-------------|---------|
|
||||
| `dapr.sidecar.ip` | IP address for the Dapr sidecar | `localhost` |
|
||||
| `dapr.http.port` | HTTP port for the Dapr sidecar | `3500` |
|
||||
| `dapr.grpc.port` | gRPC port for the Dapr sidecar | `50001` |
|
||||
| `dapr.grpc.tls.cert.path` | Path to the gRPC TLS certificate | `null` |
|
||||
| `dapr.grpc.tls.key.path` | Path to the gRPC TLS key | `null` |
|
||||
| `dapr.grpc.tls.ca.path` | Path to the gRPC TLS CA certificate | `null` |
|
||||
| `dapr.grpc.tls.insecure` | Whether to use insecure TLS mode | `false` |
|
||||
| `dapr.grpc.endpoint` | gRPC endpoint for remote sidecar | `null` |
|
||||
| `dapr.grpc.enable.keep.alive` | Whether to enable gRPC keepalive | `false` |
|
||||
| `dapr.grpc.keep.alive.time.seconds` | gRPC keepalive time in seconds | `10` |
|
||||
| `dapr.grpc.keep.alive.timeout.seconds` | gRPC keepalive timeout in seconds | `5` |
|
||||
| `dapr.grpc.keep.alive.without.calls` | Whether to keep gRPC connection alive without calls | `true` |
|
||||
| `dapr.http.endpoint` | HTTP endpoint for remote sidecar | `null` |
|
||||
| `dapr.api.maxRetries` | Maximum number of retries for API calls | `0` |
|
||||
| `dapr.api.timeoutMilliseconds` | Timeout for API calls in milliseconds | `0` |
|
||||
| `dapr.api.token` | API token for authentication | `null` |
|
||||
| `dapr.string.charset` | String encoding used in the SDK | `UTF-8` |
|
||||
| `dapr.http.client.readTimeoutSeconds` | Timeout in seconds for HTTP client reads | `60` |
|
||||
| `dapr.http.client.maxRequests` | Maximum number of concurrent HTTP requests | `1024` |
|
||||
| `dapr.http.client.maxIdleConnections` | Maximum number of idle HTTP connections | `128` |
|
||||
|
||||
## Property Resolution Order
|
||||
|
||||
Properties are resolved in the following order:
|
||||
1. Override values (if provided when creating a Properties instance)
|
||||
2. System properties (set via `-D`)
|
||||
3. Environment variables
|
||||
4. Default values
|
||||
|
||||
The SDK checks each source in order. If a value is invalid for the property type (e.g., non-numeric for a numeric property), the SDK will log a warning and try the next source. For example:
|
||||
|
||||
```bash
|
||||
# Invalid boolean value - will be ignored
|
||||
java -Ddapr.grpc.enable.keep.alive=not-a-boolean -jar myapp.jar
|
||||
|
||||
# Valid boolean value - will be used
|
||||
export DAPR_GRPC_ENABLE_KEEP_ALIVE=false
|
||||
```
|
||||
|
||||
In this case, the environment variable is used because the system property value is invalid. However, if both values are valid, the system property takes precedence:
|
||||
|
||||
```bash
|
||||
# Valid boolean value - will be used
|
||||
java -Ddapr.grpc.enable.keep.alive=true -jar myapp.jar
|
||||
|
||||
# Valid boolean value - will be ignored
|
||||
export DAPR_GRPC_ENABLE_KEEP_ALIVE=false
|
||||
```
|
||||
|
||||
Override values can be set using the `DaprClientBuilder` in two ways:
|
||||
|
||||
1. Using individual property overrides (recommended for most cases):
|
||||
```java
|
||||
import io.dapr.config.Properties;
|
||||
|
||||
// Set a single property override
|
||||
DaprClient client = new DaprClientBuilder()
|
||||
.withPropertyOverride(Properties.GRPC_ENABLE_KEEP_ALIVE, "true")
|
||||
.build();
|
||||
|
||||
// Or set multiple property overrides
|
||||
DaprClient client = new DaprClientBuilder()
|
||||
.withPropertyOverride(Properties.GRPC_ENABLE_KEEP_ALIVE, "true")
|
||||
.withPropertyOverride(Properties.HTTP_CLIENT_READ_TIMEOUT_SECONDS, "120")
|
||||
.build();
|
||||
```
|
||||
|
||||
2. Using a Properties instance (useful when you have many properties to set at once):
|
||||
```java
|
||||
// Create a map of property overrides
|
||||
Map<String, String> overrides = new HashMap<>();
|
||||
overrides.put("dapr.grpc.enable.keep.alive", "true");
|
||||
overrides.put("dapr.http.client.readTimeoutSeconds", "120");
|
||||
|
||||
// Create a Properties instance with overrides
|
||||
Properties properties = new Properties(overrides);
|
||||
|
||||
// Use these properties when creating a client
|
||||
DaprClient client = new DaprClientBuilder()
|
||||
.withProperties(properties)
|
||||
.build();
|
||||
```
|
||||
|
||||
For most use cases, you'll use system properties or environment variables. Override values are primarily used when you need different property values for different instances of the SDK in the same application.
|
||||
|
||||
## Proxy Configuration
|
||||
|
||||
You can configure proxy settings for your Java application using system properties. These are standard Java system properties that are part of Java's networking layer (`java.net` package), not specific to Dapr. They are used by Java's networking stack, including the HTTP client that Dapr's SDK uses.
|
||||
|
||||
For detailed information about Java's proxy configuration, including all available properties and their usage, see the [Java Networking Properties documentation](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/doc-files/net-properties.html).
|
||||
|
||||
|
||||
For example, here's how to configure a proxy:
|
||||
|
||||
```bash
|
||||
# Configure HTTP proxy - replace with your actual proxy server details
|
||||
java -Dhttp.proxyHost=your-proxy-server.com -Dhttp.proxyPort=8080 -jar myapp.jar
|
||||
|
||||
# Configure HTTPS proxy - replace with your actual proxy server details
|
||||
java -Dhttps.proxyHost=your-proxy-server.com -Dhttps.proxyPort=8443 -jar myapp.jar
|
||||
```
|
||||
|
||||
Replace `your-proxy-server.com` with your actual proxy server hostname or IP address, and adjust the port numbers to match your proxy server configuration.
|
||||
|
||||
These proxy settings will affect all HTTP/HTTPS connections made by your Java application, including connections to the Dapr sidecar.
|
|
@ -38,7 +38,7 @@ Run the following command to install the requirements for running the jobs examp
|
|||
mvn clean install -DskipTests
|
||||
```
|
||||
|
||||
From the Java SDK root directory, navigate to the Dapr Jobs example.
|
||||
From the Java SDK root directory, navigate to the examples' directory.
|
||||
|
||||
```bash
|
||||
cd examples
|
||||
|
|
|
@ -8,34 +8,37 @@ description: How to get started with Dapr and Spring Boot
|
|||
|
||||
By combining Dapr and Spring Boot, we can create infrastructure independent Java applications that can be deployed across different environments, supporting a wide range of on-premises and cloud provider services.
|
||||
|
||||
First, we will start with a simple integration covering the `DaprClient` and the [Testcontainers](https://testcontainers.com/) integration, to then use Spring and Spring Boot mechanisms and programming model to leverage the Dapr APIs under the hood. This help teams to remove dependencies such as clients and drivers required to connect to environment specific infrastructure (databases, key-value stores, message brokers, configuration/secret stores, etc.)
|
||||
First, we will start with a simple integration covering the `DaprClient` and the [Testcontainers](https://testcontainers.com/) integration, to then use Spring and Spring Boot mechanisms and programming model to leverage the Dapr APIs under the hood. This helps teams to remove dependencies such as clients and drivers required to connect to environment-specific infrastructure (databases, key-value stores, message brokers, configuration/secret stores, etc)
|
||||
|
||||
{{% alert title="Note" color="primary" %}}
|
||||
The Spring Boot integration explained in this page is still alpha, hence most artifacts are labeled with 0.13.0.
|
||||
The Spring Boot integration requires Spring Boot 3.x+ to work. This will not work with Spring Boot 2.x.
|
||||
The Spring Boot integration remains in alpha. We need your help and feedback to graduate it.
|
||||
Please join the [#java-sdk discord channel](https://discord.com/channels/778680217417809931/778749797242765342) discussion or open issues in the [dapr/java-sdk](https://github.com/dapr/java-sdk/issues).
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
|
||||
## Adding the Dapr and Spring Boot integration to your project
|
||||
|
||||
If you already have a Spring Boot application (Spring Boot 3.x+), you can directly add the following dependencies to your project:
|
||||
|
||||
If you already have a Spring Boot application, you can directly add the following dependencies to your project:
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
<version>0.14.1</version>
|
||||
<version>0.x.x</version> // see below for the latest versions
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter-test</artifactId>
|
||||
<version>0.14.1</version>
|
||||
<version>0.x.x</version> // see below for the latest versions
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
By adding these dependencies you can:
|
||||
You can find the [latest released version here](https://central.sonatype.com/artifact/io.dapr.spring/dapr-spring-boot-starter).
|
||||
|
||||
By adding these dependencies, you can:
|
||||
- Autowire a `DaprClient` to use inside your applications
|
||||
- Use the Spring Data and Messaging abstractions and programming model that uses the Dapr APIs under the hood
|
||||
- Improve your inner-development loop by relying on [Testcontainers](https://testcontainers.com/) to bootstrap Dapr Control plane services and default components
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-examples</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-examples</name>
|
||||
|
||||
<properties>
|
||||
|
@ -22,7 +22,8 @@
|
|||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<spotbugs.fail>false</spotbugs.fail>
|
||||
<opentelemetry.version>0.14.0</opentelemetry.version>
|
||||
<opentelemetry.version>1.41.0</opentelemetry.version>
|
||||
<zipkin.version>3.4.0</zipkin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -77,11 +78,26 @@
|
|||
<artifactId>opentelemetry-exporter-logging</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-metrics</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-zipkin</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter</artifactId>
|
||||
<version>${zipkin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-urlconnection</artifactId>
|
||||
<version>${zipkin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||
public class DaprApplication {
|
||||
|
||||
/**
|
||||
* Starts Dapr's callback in a given port and specified protocal.
|
||||
* Starts Dapr's callback in a given port and specified protocol.
|
||||
*
|
||||
* @param port Port to listen to.
|
||||
* @param protocal select Http or gRPC to run.
|
||||
|
|
|
@ -20,9 +20,10 @@ import io.opentelemetry.api.trace.Tracer;
|
|||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.ContextPropagators;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import io.opentelemetry.context.propagation.TextMapSetter;
|
||||
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -59,18 +60,23 @@ public class OpenTelemetryConfig {
|
|||
* Creates an opentelemetry instance.
|
||||
* @return OpenTelemetry.
|
||||
*/
|
||||
public static OpenTelemetry createOpenTelemetry() {
|
||||
public static OpenTelemetrySdk createOpenTelemetry() {
|
||||
// Only exports to Zipkin if it is up. Otherwise, ignore it.
|
||||
// This is helpful to avoid exceptions for examples that do not require Zipkin.
|
||||
if (isZipkinUp()) {
|
||||
Resource serviceResource = Resource.getDefault()
|
||||
.toBuilder()
|
||||
.put("service.name", InvokeClient.class.getName()) // Use ResourceAttributes constant
|
||||
.build();
|
||||
String httpUrl = String.format("http://localhost:%d", ZIPKIN_PORT);
|
||||
|
||||
ZipkinSpanExporter zipkinExporter =
|
||||
ZipkinSpanExporter.builder()
|
||||
.setEndpoint(httpUrl + ENDPOINT_V2_SPANS)
|
||||
.setServiceName(InvokeClient.class.getName())
|
||||
.build();
|
||||
|
||||
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
|
||||
.setResource(serviceResource)
|
||||
.addSpanProcessor(SimpleSpanProcessor.create(zipkinExporter))
|
||||
.build();
|
||||
|
||||
|
@ -100,7 +106,7 @@ public class OpenTelemetryConfig {
|
|||
*/
|
||||
public static reactor.util.context.Context getReactorContext(Context context) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
TextMapPropagator.Setter<Map<String, String>> setter =
|
||||
TextMapSetter<Map<String, String>> setter =
|
||||
(carrier, key, value) -> map.put(key, value);
|
||||
|
||||
GlobalOpenTelemetry.getPropagators().getTextMapPropagator().inject(context, map, setter);
|
||||
|
|
|
@ -15,6 +15,7 @@ package io.dapr.examples;
|
|||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.context.propagation.TextMapGetter;
|
||||
import io.opentelemetry.context.propagation.TextMapPropagator;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -34,8 +35,8 @@ public class OpenTelemetryInterceptor implements HandlerInterceptor {
|
|||
@Autowired
|
||||
private OpenTelemetry openTelemetry;
|
||||
|
||||
private static final TextMapPropagator.Getter<HttpServletRequest> HTTP_SERVLET_REQUEST_GETTER =
|
||||
new TextMapPropagator.Getter<>() {
|
||||
private static final TextMapGetter<HttpServletRequest> HTTP_SERVLET_REQUEST_GETTER =
|
||||
new TextMapGetter<>() {
|
||||
@Override
|
||||
public Iterable<String> keys(HttpServletRequest carrier) {
|
||||
return Collections.list(carrier.getHeaderNames());
|
||||
|
|
|
@ -19,8 +19,8 @@ import io.dapr.client.DaprPreviewClient;
|
|||
import io.dapr.client.domain.BulkPublishResponse;
|
||||
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
|
||||
import io.dapr.examples.OpenTelemetryConfig;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
|
@ -55,28 +55,41 @@ public class BulkPublisher {
|
|||
* @throws Exception any exception
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
OpenTelemetry openTelemetry = OpenTelemetryConfig.createOpenTelemetry();
|
||||
Tracer tracer = openTelemetry.getTracer(BulkPublisher.class.getCanonicalName());
|
||||
Span span = tracer.spanBuilder("Bulk Publisher's Main").setSpanKind(Span.Kind.CLIENT).startSpan();
|
||||
OpenTelemetrySdk openTelemetrySdk = OpenTelemetryConfig.createOpenTelemetry();
|
||||
Tracer tracer = openTelemetrySdk.getTracer(BulkPublisher.class.getCanonicalName());
|
||||
Span span = tracer.spanBuilder("Bulk Publisher's Main").setSpanKind(SpanKind.CLIENT).startSpan();
|
||||
|
||||
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
||||
DaprClient c = (DaprClient) client;
|
||||
|
||||
c.waitForSidecar(10000);
|
||||
|
||||
try (Scope scope = span.makeCurrent()) {
|
||||
System.out.println("Using preview client...");
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
System.out.println("Constructing the list of messages to publish");
|
||||
|
||||
for (int i = 0; i < NUM_MESSAGES; i++) {
|
||||
String message = String.format("This is message #%d", i);
|
||||
|
||||
messages.add(message);
|
||||
|
||||
System.out.println("Going to publish message : " + message);
|
||||
}
|
||||
BulkPublishResponse<?> res = client.publishEvents(PUBSUB_NAME, TOPIC_NAME, "text/plain", messages)
|
||||
|
||||
BulkPublishResponse<?> res = client
|
||||
.publishEvents(PUBSUB_NAME, TOPIC_NAME, "text/plain", messages)
|
||||
.contextWrite(getReactorContext()).block();
|
||||
|
||||
System.out.println("Published the set of messages in a single call to Dapr");
|
||||
|
||||
if (res != null) {
|
||||
if (res.getFailedEntries().size() > 0) {
|
||||
// Ideally this condition will not happen in examples
|
||||
System.out.println("Some events failed to be published");
|
||||
|
||||
for (BulkPublishResponseFailedEntry<?> entry : res.getFailedEntries()) {
|
||||
System.out.println("EntryId : " + entry.getEntry().getEntryId()
|
||||
+ " Error message : " + entry.getErrorMessage());
|
||||
|
@ -86,16 +99,11 @@ public class BulkPublisher {
|
|||
throw new Exception("null response from dapr");
|
||||
}
|
||||
}
|
||||
// Close the span.
|
||||
|
||||
span.end();
|
||||
// Allow plenty of time for Dapr to export all relevant spans to the tracing infra.
|
||||
Thread.sleep(10000);
|
||||
// Shutdown the OpenTelemetry tracer.
|
||||
OpenTelemetrySdk.getGlobalTracerManagement().shutdown();
|
||||
|
||||
openTelemetrySdk.getSdkTracerProvider().shutdown();
|
||||
System.out.println("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ package io.dapr.examples.pubsub;
|
|||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.client.DaprClientBuilder;
|
||||
import io.dapr.examples.OpenTelemetryConfig;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
|
@ -51,19 +51,21 @@ public class PublisherWithTracing {
|
|||
* @throws Exception A startup Exception.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
OpenTelemetry openTelemetry = OpenTelemetryConfig.createOpenTelemetry();
|
||||
Tracer tracer = openTelemetry.getTracer(PublisherWithTracing.class.getCanonicalName());
|
||||
Span span = tracer.spanBuilder("Publisher's Main").setSpanKind(Span.Kind.CLIENT).startSpan();
|
||||
OpenTelemetrySdk openTelemetrySdk = OpenTelemetryConfig.createOpenTelemetry();
|
||||
Tracer tracer = openTelemetrySdk.getTracer(PublisherWithTracing.class.getCanonicalName());
|
||||
Span span = tracer.spanBuilder("Publisher's Main").setSpanKind(SpanKind.CLIENT).startSpan();
|
||||
|
||||
try (DaprClient client = new DaprClientBuilder().build()) {
|
||||
try (Scope scope = span.makeCurrent()) {
|
||||
for (int i = 0; i < NUM_MESSAGES; i++) {
|
||||
String message = String.format("This is message #%d", i);
|
||||
|
||||
// Publishing messages, notice the use of subscriberContext() for tracing.
|
||||
client.publishEvent(
|
||||
PUBSUB_NAME,
|
||||
TOPIC_NAME,
|
||||
message).contextWrite(getReactorContext()).block();
|
||||
|
||||
System.out.println("Published message: " + message);
|
||||
|
||||
try {
|
||||
|
@ -71,19 +73,14 @@ public class PublisherWithTracing {
|
|||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the span.
|
||||
span.end();
|
||||
|
||||
// Shutdown the OpenTelemetry tracer.
|
||||
OpenTelemetrySdk.getGlobalTracerManagement().shutdown();
|
||||
|
||||
// This is an example, so for simplicity we are just exiting here.
|
||||
// Normally a dapr app would be a web service and not exit main.
|
||||
openTelemetrySdk.getSdkTracerProvider().shutdown();
|
||||
System.out.println("Done.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ import io.dapr.client.domain.HttpExtension;
|
|||
import io.dapr.client.domain.InvokeMethodRequest;
|
||||
import io.dapr.examples.OpenTelemetryConfig;
|
||||
import io.dapr.utils.TypeRef;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.api.trace.Tracer;
|
||||
import io.opentelemetry.context.Scope;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
|
@ -48,10 +48,10 @@ public class InvokeClient {
|
|||
* @param args Messages to be sent as request for the invoke API.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
final OpenTelemetry openTelemetry = OpenTelemetryConfig.createOpenTelemetry();
|
||||
final Tracer tracer = openTelemetry.getTracer(InvokeClient.class.getCanonicalName());
|
||||
OpenTelemetrySdk openTelemetrySdk = OpenTelemetryConfig.createOpenTelemetry();
|
||||
Tracer tracer = openTelemetrySdk.getTracer(InvokeClient.class.getCanonicalName());
|
||||
Span span = tracer.spanBuilder("Example's Main").setSpanKind(SpanKind.CLIENT).startSpan();
|
||||
|
||||
Span span = tracer.spanBuilder("Example's Main").setSpanKind(Span.Kind.CLIENT).startSpan();
|
||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||
for (String message : args) {
|
||||
try (Scope scope = span.makeCurrent()) {
|
||||
|
@ -70,15 +70,11 @@ public class InvokeClient {
|
|||
}).contextWrite(getReactorContext()).block();
|
||||
}
|
||||
}
|
||||
|
||||
span.end();
|
||||
openTelemetrySdk.getSdkTracerProvider().shutdown();
|
||||
Validation.validate();
|
||||
System.out.println("Done");
|
||||
}
|
||||
span.end();
|
||||
shutdown();
|
||||
System.out.println("Done");
|
||||
}
|
||||
|
||||
private static void shutdown() throws Exception {
|
||||
OpenTelemetrySdk.getGlobalTracerManagement().shutdown();
|
||||
Validation.validate();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ Those examples contain the following workflow patterns:
|
|||
2. [Fan-out/Fan-in Pattern](#fan-outfan-in-pattern)
|
||||
3. [Continue As New Pattern](#continue-as-new-pattern)
|
||||
4. [External Event Pattern](#external-event-pattern)
|
||||
5. [child-workflow Pattern](#child-workflow-pattern)
|
||||
5. [Child-workflow Pattern](#child-workflow-pattern)
|
||||
6. [Compensation Pattern](#compensation-pattern)
|
||||
|
||||
### Chaining Pattern
|
||||
In the chaining pattern, a sequence of activities executes in a specific order.
|
||||
|
@ -353,7 +354,7 @@ dapr run --app-id demoworkflowworker --resources-path ./components/workflows --
|
|||
```
|
||||
```sh
|
||||
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.continueasnew.DemoContinueAsNewClient
|
||||
````
|
||||
```
|
||||
|
||||
You will see the logs from worker showing the `CleanUpActivity` is invoked every 10 seconds after previous one is finished:
|
||||
```text
|
||||
|
@ -419,7 +420,6 @@ client.raiseEvent(instanceId, "Approval", true);
|
|||
|
||||
Start the workflow and client using the following commands:
|
||||
|
||||
ex
|
||||
```sh
|
||||
dapr run --app-id demoworkflowworker --resources-path ./components/workflows -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.externalevent.DemoExternalEventWorker
|
||||
```
|
||||
|
@ -444,7 +444,7 @@ Started a new external-event model workflow with instance ID: 23410d96-1afe-4698
|
|||
workflow instance with ID: 23410d96-1afe-4698-9fcd-c01c1e0db255 completed.
|
||||
```
|
||||
|
||||
### child-workflow Pattern
|
||||
### Child-workflow Pattern
|
||||
The child-workflow pattern allows you to call a workflow from another workflow.
|
||||
|
||||
The `DemoWorkflow` class defines the workflow. It calls a child-workflow `DemoChildWorkflow` to do the work. See the code snippet below:
|
||||
|
@ -540,3 +540,171 @@ The log from client:
|
|||
Started a new child-workflow model workflow with instance ID: c2fb9c83-435b-4b55-bdf1-833b39366cfb
|
||||
workflow instance with ID: c2fb9c83-435b-4b55-bdf1-833b39366cfb completed with result: !wolfkroW rpaD olleH
|
||||
```
|
||||
|
||||
### Compensation Pattern
|
||||
The compensation pattern is used to "undo" or "roll back" previously completed steps if a later step fails. This pattern is particularly useful in scenarios where you need to ensure that all resources are properly cleaned up even if the process fails.
|
||||
|
||||
The example simulates a trip booking workflow that books a flight, hotel, and car. If any step fails, the workflow will automatically compensate (cancel) the previously completed bookings in reverse order.
|
||||
|
||||
The `BookTripWorkflow` class defines the workflow. It orchestrates the booking process and handles compensation if any step fails. See the code snippet below:
|
||||
```java
|
||||
public class BookTripWorkflow extends Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
List<String> compensations = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// Book flight
|
||||
String flightResult = ctx.callActivity(BookFlightActivity.class.getName(), String.class).await();
|
||||
ctx.getLogger().info("Flight booking completed: " + flightResult);
|
||||
compensations.add(CancelFlightActivity.class.getName());
|
||||
|
||||
// Book hotel
|
||||
String hotelResult = ctx.callActivity(BookHotelActivity.class.getName(), String.class).await();
|
||||
ctx.getLogger().info("Hotel booking completed: " + hotelResult);
|
||||
compensations.add(CancelHotelActivity.class.getName());
|
||||
|
||||
// Book car
|
||||
String carResult = ctx.callActivity(BookCarActivity.class.getName(), String.class).await();
|
||||
ctx.getLogger().info("Car booking completed: " + carResult);
|
||||
compensations.add(CancelCarActivity.class.getName());
|
||||
|
||||
} catch (Exception e) {
|
||||
ctx.getLogger().info("******** executing compensation logic ********");
|
||||
// Execute compensations in reverse order
|
||||
Collections.reverse(compensations);
|
||||
for (String compensation : compensations) {
|
||||
try {
|
||||
ctx.callActivity(compensation, String.class).await();
|
||||
} catch (Exception ex) {
|
||||
ctx.getLogger().error("Error during compensation: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
ctx.complete("Workflow failed, compensation applied");
|
||||
return;
|
||||
}
|
||||
ctx.complete("All bookings completed successfully");
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each activity class (`BookFlightActivity`, `BookHotelActivity`, `BookCarActivity`) implements the booking logic, while their corresponding compensation activities (`CancelFlightActivity`, `CancelHotelActivity`, `CancelCarActivity`) implement the cancellation logic.
|
||||
|
||||
<!-- STEP
|
||||
name: Run Compensation Pattern workflow worker
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- "Registered Workflow: BookTripWorkflow"
|
||||
- "Registered Activity: BookFlightActivity"
|
||||
- "Registered Activity: CancelFlightActivity"
|
||||
- "Registered Activity: BookHotelActivity"
|
||||
- "Registered Activity: CancelHotelActivity"
|
||||
- "Registered Activity: BookCarActivity"
|
||||
- "Registered Activity: CancelCarActivity"
|
||||
- "Successfully built dapr workflow runtime"
|
||||
- "Start workflow runtime"
|
||||
- "Durable Task worker is connecting to sidecar at 127.0.0.1:50001."
|
||||
|
||||
- "Starting Workflow: io.dapr.examples.workflows.compensation.BookTripWorkflow"
|
||||
- "Starting Activity: io.dapr.examples.workflows.compensation.BookFlightActivity"
|
||||
- "Activity completed with result: Flight booked successfully"
|
||||
- "Flight booking completed: Flight booked successfully"
|
||||
- "Starting Activity: io.dapr.examples.workflows.compensation.BookHotelActivity"
|
||||
- "Simulating hotel booking process..."
|
||||
- "Activity completed with result: Hotel booked successfully"
|
||||
- "Hotel booking completed: Hotel booked successfully"
|
||||
- "Starting Activity: io.dapr.examples.workflows.compensation.BookCarActivity"
|
||||
- "Forcing Failure to trigger compensation for activity: io.dapr.examples.workflows.compensation.BookCarActivity"
|
||||
- "******** executing compensation logic ********"
|
||||
- "Activity failed: Task 'io.dapr.examples.workflows.compensation.BookCarActivity' (#2) failed with an unhandled exception: Failed to book car"
|
||||
- "Starting Activity: io.dapr.examples.workflows.compensation.CancelHotelActivity"
|
||||
- "Activity completed with result: Hotel canceled successfully"
|
||||
- "Starting Activity: io.dapr.examples.workflows.compensation.CancelFlightActivity"
|
||||
- "Activity completed with result: Flight canceled successfully"
|
||||
background: true
|
||||
sleep: 60
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
Execute the following script in order to run the BookTripWorker:
|
||||
```sh
|
||||
dapr run --app-id book-trip-worker --resources-path ./components/workflows --dapr-grpc-port 50001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.compensation.BookTripWorker
|
||||
```
|
||||
|
||||
Once running, execute the following script to run the BookTripClient:
|
||||
```sh
|
||||
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.compensation.BookTripClient
|
||||
```
|
||||
<!-- END_STEP -->
|
||||
|
||||
The output demonstrates:
|
||||
1. The workflow starts and successfully books a flight
|
||||
2. Then successfully books a hotel
|
||||
3. When attempting to book a car, it fails (intentionally)
|
||||
4. The compensation logic triggers, canceling the hotel and flight in reverse order
|
||||
5. The workflow completes with a status indicating the compensation was applied
|
||||
|
||||
Key Points:
|
||||
1. Each successful booking step adds its compensation action to an ArrayList
|
||||
2. If an error occurs, the list of compensations is reversed and executed in reverse order
|
||||
3. The workflow ensures that all resources are properly cleaned up even if the process fails
|
||||
4. Each activity simulates work with a short delay for demonstration purposes
|
||||
|
||||
|
||||
### Suspend/Resume Pattern
|
||||
|
||||
Workflow instances can be suspended and resumed. This example shows how to use the suspend and resume commands.
|
||||
|
||||
For testing the suspend and resume operations we will use the same workflow definition used by the DemoExternalEventWorkflow.
|
||||
|
||||
Start the workflow and client using the following commands:
|
||||
|
||||
|
||||
<!-- STEP
|
||||
name: Run Suspend/Resume workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- "Waiting for approval..."
|
||||
- "Suspending Workflow Instance"
|
||||
- "Workflow Instance Status: SUSPENDED"
|
||||
- "Let's resume the Workflow Instance before sending the external event"
|
||||
- "Workflow Instance Status: RUNNING"
|
||||
- "Now that the instance is RUNNING again, lets send the external event."
|
||||
- "approval granted - do the approved action"
|
||||
- "Starting Activity: io.dapr.examples.workflows.externalevent.ApproveActivity"
|
||||
- "Running approval activity..."
|
||||
- "approval-activity finished"
|
||||
background: true
|
||||
sleep: 60
|
||||
timeout_seconds: 60
|
||||
-->
|
||||
|
||||
```sh
|
||||
dapr run --app-id demoworkflowworker --resources-path ./components/workflows --dapr-grpc-port 50001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.suspendresume.DemoSuspendResumeWorker
|
||||
```
|
||||
|
||||
```sh
|
||||
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.suspendresume.DemoSuspendResumeClient
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
The worker logs:
|
||||
```text
|
||||
== APP == 2023-11-07 16:01:23,279 {HH:mm:ss.SSS} [main] INFO io.dapr.workflows.WorkflowContext - Starting Workflow: io.dapr.examples.workflows.suspendresume.DemoExternalEventWorkflow
|
||||
== APP == 2023-11-07 16:01:23,279 {HH:mm:ss.SSS} [main] INFO io.dapr.workflows.WorkflowContext - Waiting for approval...
|
||||
== APP == 2023-11-07 16:01:23,324 {HH:mm:ss.SSS} [main] INFO io.dapr.workflows.WorkflowContext - approval granted - do the approved action
|
||||
== APP == 2023-11-07 16:01:23,348 {HH:mm:ss.SSS} [main] INFO i.d.e.w.e.ApproveActivity - Starting Activity: io.dapr.examples.workflows.externalevent.ApproveActivity
|
||||
== APP == 2023-11-07 16:01:23,348 {HH:mm:ss.SSS} [main] INFO i.d.e.w.e.ApproveActivity - Running approval activity...
|
||||
== APP == 2023-11-07 16:01:28,410 {HH:mm:ss.SSS} [main] INFO io.dapr.workflows.WorkflowContext - approval-activity finished
|
||||
```
|
||||
|
||||
The client log:
|
||||
```text
|
||||
Started a new external-event model workflow with instance ID: 23410d96-1afe-4698-9fcd-c01c1e0db255
|
||||
workflow instance with ID: 23410d96-1afe-4698-9fcd-c01c1e0db255 completed.
|
||||
```
|
|
@ -13,8 +13,13 @@ limitations under the License.
|
|||
|
||||
package io.dapr.examples.workflows.childworkflow;
|
||||
|
||||
import io.dapr.durabletask.interruption.OrchestratorBlockedException;
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import io.dapr.workflows.WorkflowTaskOptions;
|
||||
import io.dapr.workflows.WorkflowTaskRetryPolicy;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class DemoChildWorkflow implements Workflow {
|
||||
@Override
|
||||
|
@ -22,11 +27,18 @@ public class DemoChildWorkflow implements Workflow {
|
|||
return ctx -> {
|
||||
ctx.getLogger().info("Starting ChildWorkflow: " + ctx.getName());
|
||||
|
||||
WorkflowTaskRetryPolicy policy = WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setFirstRetryInterval(Duration.ofSeconds(1))
|
||||
.setMaxNumberOfAttempts(10)
|
||||
.build();
|
||||
|
||||
WorkflowTaskOptions options = new WorkflowTaskOptions(policy);
|
||||
|
||||
var childWorkflowInput = ctx.getInput(String.class);
|
||||
ctx.getLogger().info("ChildWorkflow received input: " + childWorkflowInput);
|
||||
|
||||
ctx.getLogger().info("ChildWorkflow is calling Activity: " + ReverseActivity.class.getName());
|
||||
String result = ctx.callActivity(ReverseActivity.class.getName(), childWorkflowInput, String.class).await();
|
||||
String result = ctx.callActivity(ReverseActivity.class.getName(), childWorkflowInput, options, String.class).await();
|
||||
|
||||
ctx.getLogger().info("ChildWorkflow finished with: " + result);
|
||||
ctx.complete(result);
|
||||
|
|
|
@ -32,6 +32,7 @@ public class DemoChildWorkflowWorker {
|
|||
|
||||
// Build and then start the workflow runtime pulling and executing tasks
|
||||
WorkflowRuntime runtime = builder.build();
|
||||
runtime.start();
|
||||
System.out.println("Start workflow runtime");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BookCarActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(BookCarActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
// Simulate work
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
logger.info("Forcing Failure to trigger compensation for activity: " + ctx.getName());
|
||||
|
||||
// force the compensation
|
||||
throw new RuntimeException("Failed to book car");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BookFlightActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(BookFlightActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
// Simulate work
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String result = "Flight booked successfully";
|
||||
logger.info("Activity completed with result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class BookHotelActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(BookHotelActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
logger.info("Simulating hotel booking process...");
|
||||
|
||||
// Simulate some work
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
String result = "Hotel booked successfully";
|
||||
logger.info("Activity completed with result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||
import io.dapr.workflows.client.WorkflowInstanceStatus;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class BookTripClient {
|
||||
public static void main(String[] args) {
|
||||
try (DaprWorkflowClient client = new DaprWorkflowClient()) {
|
||||
String instanceId = client.scheduleNewWorkflow(BookTripWorkflow.class);
|
||||
System.out.printf("Started a new trip booking workflow with instance ID: %s%n", instanceId);
|
||||
|
||||
WorkflowInstanceStatus status = client.waitForInstanceCompletion(instanceId, Duration.ofMinutes(30), true);
|
||||
System.out.printf("Workflow instance with ID: %s completed with status: %s%n", instanceId, status);
|
||||
System.out.printf("Workflow output: %s%n", status.getSerializedOutput());
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.runtime.WorkflowRuntime;
|
||||
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
|
||||
|
||||
public class BookTripWorker {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Register the Workflow with the builder
|
||||
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder()
|
||||
.registerWorkflow(BookTripWorkflow.class)
|
||||
.registerActivity(BookFlightActivity.class)
|
||||
.registerActivity(CancelFlightActivity.class)
|
||||
.registerActivity(BookHotelActivity.class)
|
||||
.registerActivity(CancelHotelActivity.class)
|
||||
.registerActivity(BookCarActivity.class)
|
||||
.registerActivity(CancelCarActivity.class);
|
||||
|
||||
// Build and start the workflow runtime
|
||||
try (WorkflowRuntime runtime = builder.build()) {
|
||||
System.out.println("Start workflow runtime");
|
||||
runtime.start();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.durabletask.TaskFailedException;
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import io.dapr.workflows.WorkflowTaskOptions;
|
||||
import io.dapr.workflows.WorkflowTaskRetryPolicy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.time.Duration;
|
||||
|
||||
public class BookTripWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
List<String> compensations = new ArrayList<>();
|
||||
|
||||
// Define retry policy for compensation activities
|
||||
WorkflowTaskRetryPolicy compensationRetryPolicy = WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setFirstRetryInterval(Duration.ofSeconds(1))
|
||||
.setMaxNumberOfAttempts(3)
|
||||
.build();
|
||||
|
||||
WorkflowTaskOptions compensationOptions = new WorkflowTaskOptions(compensationRetryPolicy);
|
||||
|
||||
try {
|
||||
// Book flight
|
||||
String flightResult = ctx.callActivity(BookFlightActivity.class.getName(), null, String.class).await();
|
||||
ctx.getLogger().info("Flight booking completed: {}", flightResult);
|
||||
compensations.add("CancelFlight");
|
||||
|
||||
// Book hotel
|
||||
String hotelResult = ctx.callActivity(BookHotelActivity.class.getName(), null, String.class).await();
|
||||
ctx.getLogger().info("Hotel booking completed: {}", hotelResult);
|
||||
compensations.add("CancelHotel");
|
||||
|
||||
// Book car
|
||||
String carResult = ctx.callActivity(BookCarActivity.class.getName(), null, String.class).await();
|
||||
ctx.getLogger().info("Car booking completed: {}", carResult);
|
||||
compensations.add("CancelCar");
|
||||
|
||||
String result = String.format("%s, %s, %s", flightResult, hotelResult, carResult);
|
||||
ctx.getLogger().info("Trip booked successfully: {}", result);
|
||||
ctx.complete(result);
|
||||
|
||||
} catch (TaskFailedException e) {
|
||||
ctx.getLogger().info("******** executing compensation logic ********");
|
||||
ctx.getLogger().error("Activity failed: {}", e.getMessage());
|
||||
|
||||
// Execute compensations in reverse order
|
||||
Collections.reverse(compensations);
|
||||
for (String compensation : compensations) {
|
||||
try {
|
||||
switch (compensation) {
|
||||
case "CancelCar":
|
||||
String carCancelResult = ctx.callActivity(
|
||||
CancelCarActivity.class.getName(),
|
||||
null,
|
||||
compensationOptions,
|
||||
String.class).await();
|
||||
ctx.getLogger().info("Car cancellation completed: {}", carCancelResult);
|
||||
break;
|
||||
|
||||
case "CancelHotel":
|
||||
String hotelCancelResult = ctx.callActivity(
|
||||
CancelHotelActivity.class.getName(),
|
||||
null,
|
||||
compensationOptions,
|
||||
String.class).await();
|
||||
ctx.getLogger().info("Hotel cancellation completed: {}", hotelCancelResult);
|
||||
break;
|
||||
|
||||
case "CancelFlight":
|
||||
String flightCancelResult = ctx.callActivity(
|
||||
CancelFlightActivity.class.getName(),
|
||||
null,
|
||||
compensationOptions,
|
||||
String.class).await();
|
||||
ctx.getLogger().info("Flight cancellation completed: {}", flightCancelResult);
|
||||
break;
|
||||
}
|
||||
} catch (TaskFailedException ex) {
|
||||
// Only catch TaskFailedException for actual activity failures
|
||||
ctx.getLogger().error("Activity failed during compensation: {}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
ctx.complete("Workflow failed, compensation applied");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CancelCarActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CancelCarActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
// Simulate work
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String result = "Car canceled successfully";
|
||||
logger.info("Activity completed with result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CancelFlightActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CancelFlightActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
// Simulate work
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String result = "Flight canceled successfully";
|
||||
logger.info("Activity completed with result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.compensation;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CancelHotelActivity implements WorkflowActivity {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CancelHotelActivity.class);
|
||||
|
||||
@Override
|
||||
public String run(WorkflowActivityContext ctx) {
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
// Simulate work
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String result = "Hotel canceled successfully";
|
||||
logger.info("Activity completed with result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.suspendresume;
|
||||
|
||||
import io.dapr.examples.workflows.externalevent.DemoExternalEventWorkflow;
|
||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||
import io.dapr.workflows.client.WorkflowInstanceStatus;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class DemoSuspendResumeClient {
|
||||
/**
|
||||
* The main method to start the client.
|
||||
*
|
||||
* @param args Input arguments (unused).
|
||||
* @throws InterruptedException If program has been interrupted.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
try (DaprWorkflowClient client = new DaprWorkflowClient()) {
|
||||
String instanceId = client.scheduleNewWorkflow(DemoExternalEventWorkflow.class);
|
||||
System.out.printf("Started a new external-event workflow with instance ID: %s%n", instanceId);
|
||||
|
||||
|
||||
System.out.printf("Suspending Workflow Instance: %s%n", instanceId );
|
||||
client.suspendWorkflow(instanceId, "suspending workflow instance.");
|
||||
|
||||
WorkflowInstanceStatus instanceState = client.getInstanceState(instanceId, false);
|
||||
assert instanceState != null;
|
||||
System.out.printf("Workflow Instance Status: %s%n", instanceState.getRuntimeStatus().name() );
|
||||
|
||||
System.out.printf("Let's resume the Workflow Instance before sending the external event: %s%n", instanceId );
|
||||
client.resumeWorkflow(instanceId, "resuming workflow instance.");
|
||||
|
||||
instanceState = client.getInstanceState(instanceId, false);
|
||||
assert instanceState != null;
|
||||
System.out.printf("Workflow Instance Status: %s%n", instanceState.getRuntimeStatus().name() );
|
||||
|
||||
System.out.printf("Now that the instance is RUNNING again, lets send the external event. %n");
|
||||
client.raiseEvent(instanceId, "Approval", true);
|
||||
|
||||
client.waitForInstanceCompletion(instanceId, null, true);
|
||||
System.out.printf("workflow instance with ID: %s completed.", instanceId);
|
||||
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.examples.workflows.suspendresume;
|
||||
|
||||
import io.dapr.examples.workflows.externalevent.ApproveActivity;
|
||||
import io.dapr.examples.workflows.externalevent.DemoExternalEventWorkflow;
|
||||
import io.dapr.examples.workflows.externalevent.DenyActivity;
|
||||
import io.dapr.workflows.runtime.WorkflowRuntime;
|
||||
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
|
||||
|
||||
public class DemoSuspendResumeWorker {
|
||||
/**
|
||||
* The main method of this app.
|
||||
*
|
||||
* @param args The port the app will listen on.
|
||||
* @throws Exception An Exception.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Register the Workflow with the builder.
|
||||
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder().registerWorkflow(DemoExternalEventWorkflow.class);
|
||||
builder.registerActivity(ApproveActivity.class);
|
||||
builder.registerActivity(DenyActivity.class);
|
||||
|
||||
// Build and then start the workflow runtime pulling and executing tasks
|
||||
WorkflowRuntime runtime = builder.build();
|
||||
System.out.println("Start workflow runtime");
|
||||
runtime.start();
|
||||
}
|
||||
}
|
167
pom.xml
167
pom.xml
|
@ -1,13 +1,13 @@
|
|||
<project
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-parent</name>
|
||||
<description>SDK for Dapr.</description>
|
||||
<url>https://dapr.io</url>
|
||||
|
@ -18,8 +18,8 @@
|
|||
<protobuf.version>3.25.5</protobuf.version>
|
||||
<protocCommand>protoc</protocCommand>
|
||||
<dapr.proto.baseurl>https://raw.githubusercontent.com/dapr/dapr/v1.15.4/dapr/proto</dapr.proto.baseurl>
|
||||
<dapr.sdk.version>1.15.0-SNAPSHOT</dapr.sdk.version>
|
||||
<dapr.sdk.alpha.version>0.15.0-SNAPSHOT</dapr.sdk.alpha.version>
|
||||
<dapr.sdk.version>1.16.0-SNAPSHOT</dapr.sdk.version>
|
||||
<dapr.sdk.alpha.version>0.16.0-SNAPSHOT</dapr.sdk.alpha.version>
|
||||
<os-maven-plugin.version>1.7.1</os-maven-plugin.version>
|
||||
<maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
|
||||
<maven-antrun-plugin.version>1.8</maven-antrun-plugin.version>
|
||||
|
@ -51,6 +51,18 @@
|
|||
<springboot.version>3.4.3</springboot.version>
|
||||
<nexus-staging-maven-plugin.version>1.7.0</nexus-staging-maven-plugin.version>
|
||||
<assertj.version>3.27.3</assertj.version>
|
||||
<grpc.version>1.69.0</grpc.version>
|
||||
<protobuf.version>3.25.5</protobuf.version>
|
||||
<opentelemetry.version>1.41.0</opentelemetry.version>
|
||||
<logback-core.version>1.5.16</logback-core.version>
|
||||
<wiremock.version>3.9.1</wiremock.version>
|
||||
<jakarta.annotation.version>2.1.1</jakarta.annotation.version>
|
||||
<javax.annotation.version>1.3.2</javax.annotation.version>
|
||||
<commons-lang.version>3.9</commons-lang.version>
|
||||
<commons-cli.version>1.9.0</commons-cli.version>
|
||||
<commons-io.version>2.14.0</commons-io.version>
|
||||
<zipkin.version>3.4.0</zipkin.version>
|
||||
<microcks.version>0.3.1</microcks.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
|
@ -118,11 +130,11 @@
|
|||
<version>${grpc.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
|
@ -230,6 +242,139 @@
|
|||
<artifactId>spring-boot-testcontainers</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>testcontainers-dapr</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>toxiproxy</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
<version>${jakarta.annotation.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax.annotation.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>${commons-cli.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons-io.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-api</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-context</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-common</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-trace</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-metrics</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-common</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-logging</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-zipkin</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.zipkin.reporter2/zipkin-reporter -->
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter</artifactId>
|
||||
<version>${zipkin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-urlconnection</artifactId>
|
||||
<version>${zipkin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-springboot</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-workflows</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wiremock</groupId>
|
||||
<artifactId>wiremock-standalone</artifactId>
|
||||
<version>${wiremock.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-actors</name>
|
||||
<description>SDK for Actors on Dapr</description>
|
||||
|
||||
|
|
|
@ -123,7 +123,6 @@ public class ActorId extends Object implements Comparable<ActorId> {
|
|||
return new ActorId(id.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares if two actors have the same content.
|
||||
*
|
||||
|
|
|
@ -130,7 +130,6 @@ public class ActorClient implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build an instance of the Client based on the provided setup.
|
||||
*
|
||||
|
|
|
@ -42,7 +42,7 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
|
|||
private final String actorType;
|
||||
|
||||
/**
|
||||
* Serializer/deserialzier to exchange message for Actors.
|
||||
* Serializer/deserializer to exchange message for Actors.
|
||||
*/
|
||||
private final DaprObjectSerializer serializer;
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-autogen</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-autogen</name>
|
||||
<description>Auto-generated SDK for Dapr</description>
|
||||
|
||||
|
@ -55,11 +55,6 @@
|
|||
<version>${grpc.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
<version>3.9.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-springboot</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-springboot</name>
|
||||
<description>SDK extension for Springboot</description>
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ limitations under the License.
|
|||
|
||||
package io.dapr.springboot;
|
||||
|
||||
|
||||
import io.dapr.actors.runtime.ActorRuntime;
|
||||
import io.dapr.serializer.DefaultObjectSerializer;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -124,5 +123,4 @@ public class DaprController {
|
|||
@RequestBody(required = false) byte[] body) {
|
||||
return ActorRuntime.getInstance().invokeReminder(type, id, reminder, body);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -123,7 +123,6 @@ class DaprTopicSubscription {
|
|||
this(pubsubName, topic, route, "", routes, metadata, bulkSubscribe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a subscription topic.
|
||||
*
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-tests</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-tests</name>
|
||||
<description>Tests for Dapr's Java SDK - not to be published as a jar.</description>
|
||||
|
||||
|
@ -22,17 +22,10 @@
|
|||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
<dapr.sdk.version>1.15.0-SNAPSHOT</dapr.sdk.version>
|
||||
<dapr.sdk.alpha.version>0.15.0-SNAPSHOT</dapr.sdk.alpha.version>
|
||||
<dapr.sdk.version>1.16.0-SNAPSHOT</dapr.sdk.version>
|
||||
<dapr.sdk.alpha.version>0.16.0-SNAPSHOT</dapr.sdk.alpha.version>
|
||||
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
|
||||
<protobuf.input.directory>${project.basedir}/proto</protobuf.input.directory>
|
||||
<grpc.version>1.69.0</grpc.version>
|
||||
<protobuf.version>3.25.5</protobuf.version>
|
||||
<opentelemetry.version>1.41.0</opentelemetry.version>
|
||||
<springboot.version>3.4.3</springboot.version>
|
||||
<logback-core.version>1.5.16</logback-core.version>
|
||||
<wiremock.version>3.9.1</wiremock.version>
|
||||
<testcontainers-test.version>1.20.0</testcontainers-test.version>
|
||||
|
||||
<!-- The JaCoCo plugin was expecting coverage for this module, but since it's a test module
|
||||
and code coverage typically doesn't apply to tests, the coverage is set to 0. -->
|
||||
|
@ -67,117 +60,95 @@
|
|||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-api</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-api</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-context</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-common</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-trace</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-sdk-metrics</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-common</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-logging</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-exporter-zipkin</artifactId>
|
||||
<version>${opentelemetry.version}</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.zipkin.reporter2/zipkin-reporter -->
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-reporter</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.reporter2</groupId>
|
||||
<artifactId>zipkin-sender-urlconnection</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-actors</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-springboot</artifactId>
|
||||
<version>${dapr.sdk.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-workflows</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter-test</artifactId>
|
||||
<version>${dapr.sdk.alpha.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -191,7 +162,6 @@
|
|||
<dependency>
|
||||
<groupId>org.wiremock</groupId>
|
||||
<artifactId>wiremock-standalone</artifactId>
|
||||
<version>${wiremock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -207,37 +177,31 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.9</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${testcontainers-test.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<version>${testcontainers-test.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -258,7 +222,6 @@
|
|||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>toxiproxy</artifactId>
|
||||
<version>${testcontainers-test.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -55,7 +55,7 @@ public class ActorStateIT extends BaseIT {
|
|||
new ActorProxyBuilder(actorType, ActorProxy.class, deferClose(run.newActorClient()));
|
||||
ActorProxy proxy = proxyBuilder.build(actorId);
|
||||
|
||||
// wating for actor to be activated
|
||||
// waiting for actor to be activated
|
||||
Thread.sleep(5000);
|
||||
|
||||
// Validate conditional read works.
|
||||
|
|
|
@ -331,7 +331,7 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
|||
|
||||
|
||||
response = daprClient.getState(STATE_STORE_NAME, new State<>(stateKey, (MyData) null, null), MyData.class);
|
||||
//retrive the data wihout any etag
|
||||
//retrieve the data without any etag
|
||||
myDataResponse = response.block();
|
||||
|
||||
//review that state value changes
|
||||
|
@ -509,7 +509,7 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
|||
public void saveUpdateAndGetStateWithEtagAndStateOptionsFirstWrite() {
|
||||
final String stateKey = "keyToBeUpdatedWithEtagAndOptions";
|
||||
|
||||
//create option with concurrency with first writte and consistency of strong
|
||||
//create option with concurrency with first write and consistency of strong
|
||||
StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG,
|
||||
StateOptions.Concurrency.FIRST_WRITE);
|
||||
|
||||
|
@ -571,7 +571,7 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
|||
public void saveUpdateAndGetStateWithEtagAndStateOptionsLastWrite() {
|
||||
final String stateKey = "keyToBeUpdatedWithEtagAndOptions";
|
||||
|
||||
//create option with concurrency with first writte and consistency of strong
|
||||
//create option with concurrency with first write and consistency of strong
|
||||
StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.LAST_WRITE);
|
||||
|
||||
//create dapr client
|
||||
|
@ -599,7 +599,7 @@ public abstract class AbstractStateClientIT extends BaseIT {
|
|||
assertEquals("data in property A", myDataResponse.getValue().getPropertyA());
|
||||
assertEquals("data in property B", myDataResponse.getValue().getPropertyB());
|
||||
|
||||
//change data to be udpated
|
||||
//change data to be updated
|
||||
data.setPropertyA("data in property A2");
|
||||
data.setPropertyB("data in property B2");
|
||||
//create deferred action to update the action with options
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.actors;
|
||||
|
||||
import io.dapr.actors.ActorId;
|
||||
import io.dapr.actors.client.ActorClient;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.actors;
|
||||
import io.dapr.actors.ActorMethod;
|
||||
import io.dapr.actors.ActorType;
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.actors;
|
||||
|
||||
import io.dapr.actors.ActorId;
|
||||
import io.dapr.actors.runtime.AbstractActor;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.actors;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.actors;
|
||||
|
||||
import java.util.Map;
|
||||
|
|
@ -11,12 +11,13 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.conversations;
|
||||
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.client.domain.ConversationInput;
|
||||
import io.dapr.client.domain.ConversationRequest;
|
||||
import io.dapr.client.domain.ConversationResponse;
|
||||
import io.dapr.it.testcontainers.DaprPreviewClientConfiguration;
|
||||
import io.dapr.testcontainers.Component;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
import io.dapr.testcontainers.DaprLogLevel;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.conversations;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.core;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
import io.dapr.client.DaprClient;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.core;
|
||||
|
||||
import io.dapr.testcontainers.DaprPlacementContainer;
|
||||
import org.junit.jupiter.api.Tag;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.core;
|
||||
|
||||
import io.dapr.testcontainers.DaprSchedulerContainer;
|
||||
import org.junit.jupiter.api.Tag;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.jobs;
|
||||
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.client.domain.DeleteJobRequest;
|
||||
|
@ -19,6 +19,7 @@ import io.dapr.client.domain.GetJobRequest;
|
|||
import io.dapr.client.domain.GetJobResponse;
|
||||
import io.dapr.client.domain.JobSchedule;
|
||||
import io.dapr.client.domain.ScheduleJobRequest;
|
||||
import io.dapr.it.testcontainers.DaprPreviewClientConfiguration;
|
||||
import io.dapr.testcontainers.DaprContainer;
|
||||
import io.dapr.testcontainers.DaprLogLevel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.jobs;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -20,6 +20,7 @@ import io.dapr.testcontainers.DaprContainer;
|
|||
import io.dapr.testcontainers.DaprLogLevel;
|
||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||
import io.dapr.workflows.client.WorkflowInstanceStatus;
|
||||
import io.dapr.workflows.client.WorkflowRuntimeStatus;
|
||||
import io.dapr.workflows.runtime.WorkflowRuntime;
|
||||
import io.dapr.workflows.runtime.WorkflowRuntimeBuilder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -46,7 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
@SpringBootTest(
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
classes = {
|
||||
TestDaprWorkflowsConfiguration.class,
|
||||
TestWorkflowsConfiguration.class,
|
||||
TestWorkflowsApplication.class
|
||||
}
|
||||
)
|
||||
|
@ -117,6 +118,36 @@ public class DaprWorkflowsIT {
|
|||
assertEquals(instanceId, workflowOutput.getWorkflowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuspendAndResumeWorkflows() throws Exception {
|
||||
TestWorkflowPayload payload = new TestWorkflowPayload(new ArrayList<>());
|
||||
String instanceId = workflowClient.scheduleNewWorkflow(TestWorkflow.class, payload);
|
||||
workflowClient.waitForInstanceStart(instanceId, Duration.ofSeconds(10), false);
|
||||
|
||||
workflowClient.suspendWorkflow(instanceId, "testing suspend.");
|
||||
|
||||
|
||||
WorkflowInstanceStatus instanceState = workflowClient.getInstanceState(instanceId, false);
|
||||
assertNotNull(instanceState);
|
||||
assertEquals(WorkflowRuntimeStatus.SUSPENDED, instanceState.getRuntimeStatus());
|
||||
|
||||
workflowClient.resumeWorkflow(instanceId, "testing resume");
|
||||
|
||||
instanceState = workflowClient.getInstanceState(instanceId, false);
|
||||
assertNotNull(instanceState);
|
||||
assertEquals(WorkflowRuntimeStatus.RUNNING, instanceState.getRuntimeStatus());
|
||||
|
||||
workflowClient.raiseEvent(instanceId, "MoveForward", payload);
|
||||
|
||||
Duration timeout = Duration.ofSeconds(10);
|
||||
instanceState = workflowClient.waitForInstanceCompletion(instanceId, timeout, true);
|
||||
|
||||
assertNotNull(instanceState);
|
||||
assertEquals(WorkflowRuntimeStatus.COMPLETED, instanceState.getRuntimeStatus());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private TestWorkflowPayload deserialize(String value) throws JsonProcessingException {
|
||||
return OBJECT_MAPPER.readValue(value, TestWorkflowPayload.class);
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
@ -11,7 +11,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.testcontainers;
|
||||
package io.dapr.it.testcontainers.workflows;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.dapr.config.Properties;
|
||||
|
@ -24,7 +24,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class TestDaprWorkflowsConfiguration {
|
||||
public class TestWorkflowsConfiguration {
|
||||
@Bean
|
||||
public ObjectMapper mapper() {
|
||||
return new ObjectMapper();
|
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk-workflows</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.15.0-SNAPSHOT</version>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk-workflows</name>
|
||||
<description>SDK for Workflows on Dapr</description>
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>durabletask-client</artifactId>
|
||||
<version>1.5.2</version>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
<!--
|
||||
manually declare durabletask-client's jackson dependencies
|
||||
|
|
|
@ -347,10 +347,7 @@ public interface WorkflowContext {
|
|||
* @param zonedDateTime timestamp with specific zone when the timer should expire
|
||||
* @return a new {@code Task} that completes after the specified delay
|
||||
*/
|
||||
default Task<Void> createTimer(ZonedDateTime zonedDateTime) {
|
||||
throw new UnsupportedOperationException("This method is not implemented.");
|
||||
}
|
||||
|
||||
Task<Void> createTimer(ZonedDateTime zonedDateTime);
|
||||
|
||||
/**
|
||||
* Gets the deserialized input of the current task orchestration.
|
||||
|
|
|
@ -16,13 +16,27 @@ package io.dapr.workflows;
|
|||
public class WorkflowTaskOptions {
|
||||
|
||||
private final WorkflowTaskRetryPolicy retryPolicy;
|
||||
private final WorkflowTaskRetryHandler retryHandler;
|
||||
|
||||
public WorkflowTaskOptions(WorkflowTaskRetryPolicy retryPolicy, WorkflowTaskRetryHandler retryHandler) {
|
||||
this.retryPolicy = retryPolicy;
|
||||
this.retryHandler = retryHandler;
|
||||
}
|
||||
|
||||
public WorkflowTaskOptions(WorkflowTaskRetryPolicy retryPolicy) {
|
||||
this.retryPolicy = retryPolicy;
|
||||
this(retryPolicy, null);
|
||||
}
|
||||
|
||||
public WorkflowTaskOptions(WorkflowTaskRetryHandler retryHandler) {
|
||||
this(null, retryHandler);
|
||||
}
|
||||
|
||||
public WorkflowTaskRetryPolicy getRetryPolicy() {
|
||||
return retryPolicy;
|
||||
}
|
||||
|
||||
public WorkflowTaskRetryHandler getRetryHandler() {
|
||||
return retryHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.workflows;
|
||||
|
||||
import io.dapr.workflows.client.WorkflowFailureDetails;
|
||||
import io.dapr.workflows.runtime.DefaultWorkflowContext;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class WorkflowTaskRetryContext {
|
||||
|
||||
private final DefaultWorkflowContext workflowContext;
|
||||
private final int lastAttemptNumber;
|
||||
private final WorkflowFailureDetails lastFailure;
|
||||
private final Duration totalRetryTime;
|
||||
|
||||
/**
|
||||
* Constructor for WorkflowTaskRetryContext.
|
||||
*
|
||||
* @param workflowContext The workflow context
|
||||
* @param lastAttemptNumber The number of the previous attempt
|
||||
* @param lastFailure The failure details from the most recent failure
|
||||
* @param totalRetryTime The amount of time spent retrying
|
||||
*/
|
||||
public WorkflowTaskRetryContext(
|
||||
DefaultWorkflowContext workflowContext,
|
||||
int lastAttemptNumber,
|
||||
WorkflowFailureDetails lastFailure,
|
||||
Duration totalRetryTime) {
|
||||
this.workflowContext = workflowContext;
|
||||
this.lastAttemptNumber = lastAttemptNumber;
|
||||
this.lastFailure = lastFailure;
|
||||
this.totalRetryTime = totalRetryTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context of the current workflow.
|
||||
*
|
||||
* <p>The workflow context can be used in retry handlers to schedule timers (via the
|
||||
* {@link DefaultWorkflowContext#createTimer} methods) for implementing delays between retries. It can also be
|
||||
* used to implement time-based retry logic by using the {@link DefaultWorkflowContext#getCurrentInstant} method.
|
||||
*
|
||||
* @return the context of the parent workflow
|
||||
*/
|
||||
public DefaultWorkflowContext getWorkflowContext() {
|
||||
return this.workflowContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the details of the previous task failure, including the exception type, message, and callstack.
|
||||
*
|
||||
* @return the details of the previous task failure
|
||||
*/
|
||||
public WorkflowFailureDetails getLastFailure() {
|
||||
return this.lastFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the previous retry attempt number. This number starts at 1 and increments each time the retry handler
|
||||
* is invoked for a particular task failure.
|
||||
*
|
||||
* @return the previous retry attempt number
|
||||
*/
|
||||
public int getLastAttemptNumber() {
|
||||
return this.lastAttemptNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total amount of time spent in a retry loop for the current task.
|
||||
*
|
||||
* @return the total amount of time spent in a retry loop for the current task
|
||||
*/
|
||||
public Duration getTotalRetryTime() {
|
||||
return this.totalRetryTime;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2025 The Dapr Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package io.dapr.workflows;
|
||||
|
||||
public interface WorkflowTaskRetryHandler {
|
||||
|
||||
/**
|
||||
* Invokes retry handler logic. Return value indicates whether to continue retrying.
|
||||
*
|
||||
* @param retryContext The context of the retry
|
||||
* @return {@code true} to continue retrying or {@code false} to stop retrying.
|
||||
*/
|
||||
boolean handle(WorkflowTaskRetryContext retryContext);
|
||||
|
||||
}
|
|
@ -166,9 +166,10 @@ public final class WorkflowTaskRetryPolicy {
|
|||
* @return This builder
|
||||
*/
|
||||
public Builder setRetryTimeout(Duration retryTimeout) {
|
||||
if (retryTimeout != null && retryTimeout.compareTo(this.firstRetryInterval) < 0) {
|
||||
if (retryTimeout == null || retryTimeout.compareTo(this.firstRetryInterval) < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The value for retryTimeout must be greater than or equal to the value for firstRetryInterval.");
|
||||
"The value for retryTimeout cannot be null and"
|
||||
+ " must be greater than or equal to the value for firstRetryInterval.");
|
||||
}
|
||||
|
||||
this.retryTimeout = retryTimeout;
|
||||
|
|
|
@ -129,6 +129,26 @@ public class DaprWorkflowClient implements AutoCloseable {
|
|||
orchestrationInstanceOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend the workflow associated with the provided instance id.
|
||||
*
|
||||
* @param workflowInstanceId Workflow instance id to suspend.
|
||||
* @param reason reason for suspending the workflow instance.
|
||||
*/
|
||||
public void suspendWorkflow(String workflowInstanceId, @Nullable String reason) {
|
||||
this.innerClient.suspendInstance(workflowInstanceId, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the workflow associated with the provided instance id.
|
||||
*
|
||||
* @param workflowInstanceId Workflow instance id to resume.
|
||||
* @param reason reason for resuming the workflow instance.
|
||||
*/
|
||||
public void resumeWorkflow(String workflowInstanceId, @Nullable String reason) {
|
||||
this.innerClient.resumeInstance(workflowInstanceId, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the workflow associated with the provided instance id.
|
||||
*
|
||||
|
|
|
@ -39,4 +39,14 @@ public interface WorkflowFailureDetails {
|
|||
*/
|
||||
String getStackTrace();
|
||||
|
||||
/**
|
||||
* Checks whether the failure was caused by the provided exception class.
|
||||
*
|
||||
* @param exceptionClass the exception class to check
|
||||
* @return {@code true} if the failure was caused by the provided exception class
|
||||
*/
|
||||
default boolean isCausedBy(Class<? extends Exception> exceptionClass) {
|
||||
throw new UnsupportedOperationException("This method is not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ limitations under the License.
|
|||
package io.dapr.workflows.runtime;
|
||||
|
||||
import io.dapr.durabletask.CompositeTaskFailedException;
|
||||
import io.dapr.durabletask.RetryHandler;
|
||||
import io.dapr.durabletask.RetryPolicy;
|
||||
import io.dapr.durabletask.Task;
|
||||
import io.dapr.durabletask.TaskCanceledException;
|
||||
|
@ -21,6 +22,8 @@ import io.dapr.durabletask.TaskOptions;
|
|||
import io.dapr.durabletask.TaskOrchestrationContext;
|
||||
import io.dapr.workflows.WorkflowContext;
|
||||
import io.dapr.workflows.WorkflowTaskOptions;
|
||||
import io.dapr.workflows.WorkflowTaskRetryContext;
|
||||
import io.dapr.workflows.WorkflowTaskRetryHandler;
|
||||
import io.dapr.workflows.WorkflowTaskRetryPolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -30,6 +33,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -186,6 +190,11 @@ public class DefaultWorkflowContext implements WorkflowContext {
|
|||
return this.innerContext.createTimer(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Void> createTimer(ZonedDateTime zonedDateTime) {
|
||||
return this.innerContext.createTimer(zonedDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -228,20 +237,61 @@ public class DefaultWorkflowContext implements WorkflowContext {
|
|||
return this.innerContext.newUUID();
|
||||
}
|
||||
|
||||
private static TaskOptions toTaskOptions(WorkflowTaskOptions options) {
|
||||
private TaskOptions toTaskOptions(WorkflowTaskOptions options) {
|
||||
if (options == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WorkflowTaskRetryPolicy workflowTaskRetryPolicy = options.getRetryPolicy();
|
||||
RetryPolicy retryPolicy = toRetryPolicy(options.getRetryPolicy());
|
||||
RetryHandler retryHandler = toRetryHandler(options.getRetryHandler());
|
||||
|
||||
return new TaskOptions(retryPolicy, retryHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WorkflowTaskRetryPolicy} to a {@link RetryPolicy}.
|
||||
*
|
||||
* @param workflowTaskRetryPolicy The {@link WorkflowTaskRetryPolicy} being converted
|
||||
* @return A {@link RetryPolicy}
|
||||
*/
|
||||
private RetryPolicy toRetryPolicy(WorkflowTaskRetryPolicy workflowTaskRetryPolicy) {
|
||||
if (workflowTaskRetryPolicy == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RetryPolicy retryPolicy = new RetryPolicy(
|
||||
workflowTaskRetryPolicy.getMaxNumberOfAttempts(),
|
||||
workflowTaskRetryPolicy.getFirstRetryInterval()
|
||||
workflowTaskRetryPolicy.getMaxNumberOfAttempts(),
|
||||
workflowTaskRetryPolicy.getFirstRetryInterval()
|
||||
);
|
||||
|
||||
retryPolicy.setBackoffCoefficient(workflowTaskRetryPolicy.getBackoffCoefficient());
|
||||
retryPolicy.setRetryTimeout(workflowTaskRetryPolicy.getRetryTimeout());
|
||||
if (workflowTaskRetryPolicy.getRetryTimeout() != null) {
|
||||
retryPolicy.setRetryTimeout(workflowTaskRetryPolicy.getRetryTimeout());
|
||||
}
|
||||
|
||||
return new TaskOptions(retryPolicy);
|
||||
return retryPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WorkflowTaskRetryHandler} to a {@link RetryHandler}.
|
||||
*
|
||||
* @param workflowTaskRetryHandler The {@link WorkflowTaskRetryHandler} being converted
|
||||
* @return A {@link RetryHandler}
|
||||
*/
|
||||
private RetryHandler toRetryHandler(WorkflowTaskRetryHandler workflowTaskRetryHandler) {
|
||||
if (workflowTaskRetryHandler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return retryContext -> {
|
||||
WorkflowTaskRetryContext workflowRetryContext = new WorkflowTaskRetryContext(
|
||||
this,
|
||||
retryContext.getLastAttemptNumber(),
|
||||
new DefaultWorkflowFailureDetails(retryContext.getLastFailure()),
|
||||
retryContext.getTotalRetryTime()
|
||||
);
|
||||
|
||||
return workflowTaskRetryHandler.handle(workflowRetryContext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,17 @@ public class DefaultWorkflowFailureDetails implements WorkflowFailureDetails {
|
|||
return workflowFailureDetails.getStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the failure was caused by the provided exception class.
|
||||
*
|
||||
* @param exceptionClass the exception class to check
|
||||
* @return {@code true} if the failure was caused by the provided exception class
|
||||
*/
|
||||
@Override
|
||||
public boolean isCausedBy(Class<? extends Exception> exceptionClass) {
|
||||
return workflowFailureDetails.isCausedBy(exceptionClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return workflowFailureDetails.toString();
|
||||
|
|
|
@ -48,7 +48,6 @@ public class WorkflowActivityClassWrapper<T extends WorkflowActivity> implements
|
|||
return name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TaskActivity create() {
|
||||
return ctx -> {
|
||||
|
|
|
@ -94,4 +94,4 @@ public class WorkflowRuntime implements AutoCloseable {
|
|||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ limitations under the License.
|
|||
package io.dapr.workflows;
|
||||
|
||||
import io.dapr.durabletask.CompositeTaskFailedException;
|
||||
import io.dapr.durabletask.FailureDetails;
|
||||
import io.dapr.durabletask.RetryContext;
|
||||
import io.dapr.durabletask.RetryHandler;
|
||||
import io.dapr.durabletask.Task;
|
||||
import io.dapr.durabletask.TaskCanceledException;
|
||||
import io.dapr.durabletask.TaskOptions;
|
||||
|
@ -35,14 +38,11 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class DefaultWorkflowContextTest {
|
||||
private DefaultWorkflowContext context;
|
||||
|
@ -120,6 +120,11 @@ public class DefaultWorkflowContextTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Void> createTimer(ZonedDateTime zonedDateTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V getInput(Class<V> targetType) {
|
||||
return null;
|
||||
|
@ -265,8 +270,10 @@ public class DefaultWorkflowContextTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void createTimerWithZonedDateTimeThrowsTest() {
|
||||
assertThrows(UnsupportedOperationException.class, () -> context.createTimer(ZonedDateTime.now()));
|
||||
public void createTimerWithZonedDateTimeTest() {
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
context.createTimer(now);
|
||||
verify(mockInnerContext, times(1)).createTimer(now);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -278,7 +285,7 @@ public class DefaultWorkflowContextTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void callChildWorkflowWithOptions() {
|
||||
public void callChildWorkflowWithRetryPolicy() {
|
||||
String expectedName = "TestActivity";
|
||||
String expectedInput = "TestInput";
|
||||
String expectedInstanceId = "TestInstanceId";
|
||||
|
@ -304,6 +311,91 @@ public class DefaultWorkflowContextTest {
|
|||
|
||||
assertEquals(retryPolicy.getMaxNumberOfAttempts(), taskOptions.getRetryPolicy().getMaxNumberOfAttempts());
|
||||
assertEquals(retryPolicy.getFirstRetryInterval(), taskOptions.getRetryPolicy().getFirstRetryInterval());
|
||||
assertEquals(Duration.ZERO, taskOptions.getRetryPolicy().getRetryTimeout());
|
||||
assertNull(taskOptions.getRetryHandler());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callChildWorkflowWithRetryHandler() {
|
||||
String expectedName = "TestActivity";
|
||||
String expectedInput = "TestInput";
|
||||
String expectedInstanceId = "TestInstanceId";
|
||||
|
||||
WorkflowTaskRetryHandler retryHandler = spy(new WorkflowTaskRetryHandler() {
|
||||
@Override
|
||||
public boolean handle(WorkflowTaskRetryContext retryContext) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
WorkflowTaskOptions executionOptions = new WorkflowTaskOptions(retryHandler);
|
||||
ArgumentCaptor<TaskOptions> captor = ArgumentCaptor.forClass(TaskOptions.class);
|
||||
|
||||
context.callChildWorkflow(expectedName, expectedInput, expectedInstanceId, executionOptions, String.class);
|
||||
|
||||
verify(mockInnerContext, times(1))
|
||||
.callSubOrchestrator(
|
||||
eq(expectedName),
|
||||
eq(expectedInput),
|
||||
eq(expectedInstanceId),
|
||||
captor.capture(),
|
||||
eq(String.class)
|
||||
);
|
||||
|
||||
TaskOptions taskOptions = captor.getValue();
|
||||
|
||||
RetryHandler durableRetryHandler = taskOptions.getRetryHandler();
|
||||
RetryContext retryContext = mock(RetryContext.class, invocationOnMock -> null);
|
||||
|
||||
durableRetryHandler.handle(retryContext);
|
||||
|
||||
verify(retryHandler, times(1)).handle(any());
|
||||
assertNull(taskOptions.getRetryPolicy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callChildWorkflowWithRetryPolicyAndHandler() {
|
||||
String expectedName = "TestActivity";
|
||||
String expectedInput = "TestInput";
|
||||
String expectedInstanceId = "TestInstanceId";
|
||||
|
||||
WorkflowTaskRetryPolicy retryPolicy = WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setMaxNumberOfAttempts(1)
|
||||
.setFirstRetryInterval(Duration.ofSeconds(10))
|
||||
.build();
|
||||
|
||||
WorkflowTaskRetryHandler retryHandler = spy(new WorkflowTaskRetryHandler() {
|
||||
@Override
|
||||
public boolean handle(WorkflowTaskRetryContext retryContext) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
WorkflowTaskOptions executionOptions = new WorkflowTaskOptions(retryPolicy, retryHandler);
|
||||
ArgumentCaptor<TaskOptions> captor = ArgumentCaptor.forClass(TaskOptions.class);
|
||||
|
||||
context.callChildWorkflow(expectedName, expectedInput, expectedInstanceId, executionOptions, String.class);
|
||||
|
||||
verify(mockInnerContext, times(1))
|
||||
.callSubOrchestrator(
|
||||
eq(expectedName),
|
||||
eq(expectedInput),
|
||||
eq(expectedInstanceId),
|
||||
captor.capture(),
|
||||
eq(String.class)
|
||||
);
|
||||
|
||||
TaskOptions taskOptions = captor.getValue();
|
||||
|
||||
RetryHandler durableRetryHandler = taskOptions.getRetryHandler();
|
||||
RetryContext retryContext = mock(RetryContext.class, invocationOnMock -> null);
|
||||
|
||||
durableRetryHandler.handle(retryContext);
|
||||
|
||||
verify(retryHandler, times(1)).handle(any());
|
||||
assertEquals(retryPolicy.getMaxNumberOfAttempts(), taskOptions.getRetryPolicy().getMaxNumberOfAttempts());
|
||||
assertEquals(retryPolicy.getFirstRetryInterval(), taskOptions.getRetryPolicy().getFirstRetryInterval());
|
||||
assertEquals(Duration.ZERO, taskOptions.getRetryPolicy().getRetryTimeout());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -327,4 +419,52 @@ public class DefaultWorkflowContextTest {
|
|||
String expectedMessage = "No implementation found.";
|
||||
assertEquals(expectedMessage, runtimeException.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workflowRetryPolicyRetryTimeoutValueShouldHaveRightValueWhenBeingSet() {
|
||||
String expectedName = "TestActivity";
|
||||
String expectedInput = "TestInput";
|
||||
String expectedInstanceId = "TestInstanceId";
|
||||
WorkflowTaskRetryPolicy retryPolicy = WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setMaxNumberOfAttempts(1)
|
||||
.setFirstRetryInterval(Duration.ofSeconds(10))
|
||||
.setRetryTimeout(Duration.ofSeconds(10))
|
||||
.build();
|
||||
WorkflowTaskOptions executionOptions = new WorkflowTaskOptions(retryPolicy);
|
||||
ArgumentCaptor<TaskOptions> captor = ArgumentCaptor.forClass(TaskOptions.class);
|
||||
|
||||
context.callChildWorkflow(expectedName, expectedInput, expectedInstanceId, executionOptions, String.class);
|
||||
|
||||
verify(mockInnerContext, times(1))
|
||||
.callSubOrchestrator(
|
||||
eq(expectedName),
|
||||
eq(expectedInput),
|
||||
eq(expectedInstanceId),
|
||||
captor.capture(),
|
||||
eq(String.class)
|
||||
);
|
||||
|
||||
TaskOptions taskOptions = captor.getValue();
|
||||
|
||||
assertEquals(Duration.ofSeconds(10), taskOptions.getRetryPolicy().getRetryTimeout());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workflowRetryPolicyRetryThrowIllegalArgumentWhenNullRetryTimeoutIsSet() {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setMaxNumberOfAttempts(1)
|
||||
.setFirstRetryInterval(Duration.ofSeconds(10))
|
||||
.setRetryTimeout(null)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workflowRetryPolicyRetryThrowIllegalArgumentWhenRetryTimeoutIsLessThanMaxRetryInterval() {
|
||||
assertThrows(IllegalArgumentException.class, () -> WorkflowTaskRetryPolicy.newBuilder()
|
||||
.setMaxNumberOfAttempts(1)
|
||||
.setFirstRetryInterval(Duration.ofSeconds(10))
|
||||
.setRetryTimeout(Duration.ofSeconds(9))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,6 +217,17 @@ public class DaprWorkflowClientTest {
|
|||
expectedEventName, expectedEventPayload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void suspendResumeInstance() {
|
||||
String expectedArgument = "TestWorkflowInstanceId";
|
||||
client.suspendWorkflow(expectedArgument, "suspending workflow instance");
|
||||
client.resumeWorkflow(expectedArgument, "resuming workflow instance");
|
||||
verify(mockInnerClient, times(1)).suspendInstance(expectedArgument,
|
||||
"suspending workflow instance");
|
||||
verify(mockInnerClient, times(1)).resumeInstance(expectedArgument,
|
||||
"resuming workflow instance");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void purgeInstance() {
|
||||
String expectedArgument = "TestWorkflowInstanceId";
|
||||
|
|
21
sdk/pom.xml
21
sdk/pom.xml
|
@ -7,12 +7,12 @@
|
|||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk-parent</artifactId>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.15.0-SNAPSHOT</version>
|
||||
<version>1.16.0-SNAPSHOT</version>
|
||||
<name>dapr-sdk</name>
|
||||
<description>SDK for Dapr</description>
|
||||
|
||||
|
@ -132,6 +132,23 @@
|
|||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.70</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.70</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
<version>${grpc.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -655,7 +655,6 @@ abstract class AbstractDaprClient implements DaprClient, DaprPreviewClient {
|
|||
return this.unlock(request);
|
||||
}
|
||||
|
||||
|
||||
private List<String> filterEmptyKeys(String... keys) {
|
||||
return Arrays.stream(keys)
|
||||
.filter(key -> !key.trim().isEmpty())
|
||||
|
|
|
@ -156,7 +156,7 @@ public class DaprClientBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the GPRC Client.
|
||||
* Creates an instance of the GRPC Client.
|
||||
*
|
||||
* @return the GRPC Client.
|
||||
* @throws java.lang.IllegalStateException if either host is missing or if port is missing or a negative number.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue