mirror of https://github.com/dapr/java-sdk.git
[docs] Workflow how-to and client example (#890)
* scaffold out docs for java sdk Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * reformat toc Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * pre-test filling in doc Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * update java how to Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * add workflow to client doc Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * small edits Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * tweaks Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> * update java sdk how to and client example Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> --------- Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com> Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com>
This commit is contained in:
parent
447ff8676b
commit
b442ba46ad
|
@ -11,7 +11,7 @@ cascade:
|
||||||
github_branch: master
|
github_branch: master
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pre-requisites
|
## Prerequisites
|
||||||
|
|
||||||
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed
|
- [Dapr CLI]({{< ref install-dapr-cli.md >}}) installed
|
||||||
- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}})
|
- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}})
|
||||||
|
@ -24,9 +24,17 @@ cascade:
|
||||||
- [Maven 3.x](https://maven.apache.org/install.html)
|
- [Maven 3.x](https://maven.apache.org/install.html)
|
||||||
- [Gradle 6.x](https://gradle.org/install/)
|
- [Gradle 6.x](https://gradle.org/install/)
|
||||||
|
|
||||||
## Importing Dapr's Java SDK
|
## Import Dapr's Java SDK
|
||||||
|
|
||||||
|
Next, import the Java SDK packages to get started. Select your preferred build tool to learn how to import.
|
||||||
|
|
||||||
|
{{< tabs Maven Gradle >}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
<!--Maven-->
|
||||||
|
|
||||||
For a Maven project, add the following to your `pom.xml` file:
|
For a Maven project, add the following to your `pom.xml` file:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<project>
|
<project>
|
||||||
...
|
...
|
||||||
|
@ -55,6 +63,10 @@ For a Maven project, add the following to your `pom.xml` file:
|
||||||
...
|
...
|
||||||
</project>
|
</project>
|
||||||
```
|
```
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
<!--Gradle-->
|
||||||
|
|
||||||
For a Gradle project, add the following to your `build.gradle` file:
|
For a Gradle project, add the following to your `build.gradle` file:
|
||||||
|
|
||||||
|
@ -70,8 +82,13 @@ dependencies {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are also using Spring Boot, you may run into a common issue where the OkHttp version that the Dapr SDK uses conflicts with the one specified in the Spring Boot _Bill of Materials_.
|
{{% /codetab %}}
|
||||||
You can fix this by specifying a compatible OkHttp version in your project to match the version that the Dapr SDK uses:
|
|
||||||
|
{{< /tabs >}}
|
||||||
|
|
||||||
|
If you are also using Spring Boot, you may run into a common issue where the `OkHttp` version that the Dapr SDK uses conflicts with the one specified in the Spring Boot _Bill of Materials_.
|
||||||
|
|
||||||
|
You can fix this by specifying a compatible `OkHttp` version in your project to match the version that the Dapr SDK uses:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -81,390 +98,15 @@ You can fix this by specifying a compatible OkHttp version in your project to ma
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building blocks
|
## Try it out
|
||||||
|
|
||||||
The Java SDK allows you to interface with all of the [Dapr building blocks]({{< ref building-blocks >}}).
|
Put the Dapr Java SDK to the test. Walk through the Java quickstarts and tutorials to see Dapr in action:
|
||||||
|
|
||||||
### Invoke a service
|
| SDK samples | Description |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
| [Quickstarts]({{< ref quickstarts >}}) | Experience Dapr's API building blocks in just a few minutes using the Java SDK. |
|
||||||
|
| [SDK samples](https://github.com/dapr/java-sdk/tree/master/examples) | Clone the SDK repo to try out some examples and get started. |
|
||||||
|
|
||||||
```java
|
## More information
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
|
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
Learn more about the [Dapr Java SDK packages available to add to your Java applications](https://dapr.github.io/java-sdk/).
|
||||||
// invoke a 'GET' method (HTTP) skipping serialization: \say with a Mono<byte[]> return type
|
|
||||||
// for gRPC set HttpExtension.NONE parameters below
|
|
||||||
response = client.invokeMethod(SERVICE_TO_INVOKE, METHOD_TO_INVOKE, "{\"name\":\"World!\"}", HttpExtension.GET, byte[].class).block();
|
|
||||||
|
|
||||||
// invoke a 'POST' method (HTTP) skipping serialization: to \say with a Mono<byte[]> return type
|
|
||||||
response = client.invokeMethod(SERVICE_TO_INVOKE, METHOD_TO_INVOKE, "{\"id\":\"100\", \"FirstName\":\"Value\", \"LastName\":\"Value\"}", HttpExtension.POST, byte[].class).block();
|
|
||||||
|
|
||||||
System.out.println(new String(response));
|
|
||||||
|
|
||||||
// invoke a 'POST' method (HTTP) with serialization: \employees with a Mono<Employee> return type
|
|
||||||
Employee newEmployee = new Employee("Nigel", "Guitarist");
|
|
||||||
Employee employeeResponse = client.invokeMethod(SERVICE_TO_INVOKE, "employees", newEmployee, HttpExtension.POST, Employee.class).block();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/invoke) for code samples and instructions to try out service invocation
|
|
||||||
|
|
||||||
### Save & get application state
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import io.dapr.client.domain.State;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
|
||||||
// Save state
|
|
||||||
client.saveState(STATE_STORE_NAME, FIRST_KEY_NAME, myClass).block();
|
|
||||||
|
|
||||||
// Get state
|
|
||||||
State<MyClass> retrievedMessage = client.getState(STATE_STORE_NAME, FIRST_KEY_NAME, MyClass.class).block();
|
|
||||||
|
|
||||||
// Delete state
|
|
||||||
client.deleteState(STATE_STORE_NAME, FIRST_KEY_NAME).block();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full list of state operations visit [How-To: Get & save state]({{< ref howto-get-save-state.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/state) for code samples and instructions to try out state management
|
|
||||||
|
|
||||||
### Publish & subscribe to messages
|
|
||||||
|
|
||||||
##### Publish messages
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import io.dapr.client.domain.Metadata;
|
|
||||||
import static java.util.Collections.singletonMap;
|
|
||||||
|
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
|
||||||
client.publishEvent(PUBSUB_NAME, TOPIC_NAME, message, singletonMap(Metadata.TTL_IN_SECONDS, MESSAGE_TTL_IN_SECONDS)).block();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Subscribe to messages
|
|
||||||
|
|
||||||
```java
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.dapr.Topic;
|
|
||||||
import io.dapr.client.domain.BulkSubscribeAppResponse;
|
|
||||||
import io.dapr.client.domain.BulkSubscribeAppResponseEntry;
|
|
||||||
import io.dapr.client.domain.BulkSubscribeAppResponseStatus;
|
|
||||||
import io.dapr.client.domain.BulkSubscribeMessage;
|
|
||||||
import io.dapr.client.domain.BulkSubscribeMessageEntry;
|
|
||||||
import io.dapr.client.domain.CloudEvent;
|
|
||||||
import io.dapr.springboot.annotations.BulkSubscribe;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
public class SubscriberController {
|
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
||||||
|
|
||||||
@Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}")
|
|
||||||
@PostMapping(path = "/testingtopic")
|
|
||||||
public Mono<Void> handleMessage(@RequestBody(required = false) CloudEvent<?> cloudEvent) {
|
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
try {
|
|
||||||
System.out.println("Subscriber got: " + cloudEvent.getData());
|
|
||||||
System.out.println("Subscriber got: " + OBJECT_MAPPER.writeValueAsString(cloudEvent));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a registered publish endpoint on this app adding a topic which manage to forward undeliverable messages.
|
|
||||||
*
|
|
||||||
* @param cloudEvent The cloud event received.
|
|
||||||
* @return A message containing the time.
|
|
||||||
*/
|
|
||||||
@Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}",
|
|
||||||
deadLetterTopic = "${deadLetterProperty:deadTopic}")
|
|
||||||
@PostMapping(path = "/testingtopic")
|
|
||||||
public Mono<Void> handleMessageWithErrorHandler(@RequestBody(required = false) CloudEvent<String> cloudEvent) {
|
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
try {
|
|
||||||
System.out.println("Subscriber got: " + cloudEvent.getData());
|
|
||||||
System.out.println("Subscriber got: " + OBJECT_MAPPER.writeValueAsString(cloudEvent));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}",
|
|
||||||
rule = @Rule(match = "event.type == 'myevent.v2'", priority = 1))
|
|
||||||
@PostMapping(path = "/testingtopicV2")
|
|
||||||
public Mono<Void> handleMessageV2(@RequestBody(required = false) CloudEvent envelope) {
|
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
try {
|
|
||||||
System.out.println("Subscriber got: " + cloudEvent.getData());
|
|
||||||
System.out.println("Subscriber got: " + OBJECT_MAPPER.writeValueAsString(cloudEvent));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@BulkSubscribe()
|
|
||||||
@Topic(name = "testingtopicbulk", pubsubName = "${myAppProperty:messagebus}")
|
|
||||||
@PostMapping(path = "/testingtopicbulk")
|
|
||||||
public Mono<BulkSubscribeAppResponse> handleBulkMessage(
|
|
||||||
@RequestBody(required = false) BulkSubscribeMessage<CloudEvent<String>> bulkMessage) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
if (bulkMessage.getEntries().size() == 0) {
|
|
||||||
return new BulkSubscribeAppResponse(new ArrayList<BulkSubscribeAppResponseEntry>());
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Bulk Subscriber received " + bulkMessage.getEntries().size() + " messages.");
|
|
||||||
|
|
||||||
List<BulkSubscribeAppResponseEntry> entries = new ArrayList<BulkSubscribeAppResponseEntry>();
|
|
||||||
for (BulkSubscribeMessageEntry<?> entry : bulkMessage.getEntries()) {
|
|
||||||
try {
|
|
||||||
System.out.printf("Bulk Subscriber message has entry ID: %s\n", entry.getEntryId());
|
|
||||||
CloudEvent<?> cloudEvent = (CloudEvent<?>) entry.getEvent();
|
|
||||||
System.out.printf("Bulk Subscriber got: %s\n", cloudEvent.getData());
|
|
||||||
entries.add(new BulkSubscribeAppResponseEntry(entry.getEntryId(), BulkSubscribeAppResponseStatus.SUCCESS));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
entries.add(new BulkSubscribeAppResponseEntry(entry.getEntryId(), BulkSubscribeAppResponseStatus.RETRY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new BulkSubscribeAppResponse(entries);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Bulk Publish Messages
|
|
||||||
> Note: API is in Alpha stage
|
|
||||||
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import io.dapr.client.DaprPreviewClient;
|
|
||||||
import io.dapr.client.domain.BulkPublishResponse;
|
|
||||||
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
class Solution {
|
|
||||||
public void publishMessages() {
|
|
||||||
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
|
||||||
// Create a list of messages to publish
|
|
||||||
List<String> messages = new ArrayList<>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish list of messages using the bulk publish API
|
|
||||||
BulkPublishResponse<String> res = client.publishEvents(PUBSUB_NAME, TOPIC_NAME, "text/plain", messages).block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on publishing messages and subscribing to a topic [How-To: Publish & subscribe]({{< ref howto-publish-subscribe.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/pubsub/http) for code samples and instructions to try out pub/sub
|
|
||||||
|
|
||||||
### Interact with output bindings
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
|
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
|
||||||
// sending a class with message; BINDING_OPERATION="create"
|
|
||||||
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass).block();
|
|
||||||
|
|
||||||
// sending a plain string
|
|
||||||
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message).block();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on output bindings visit [How-To: Output bindings]({{< ref howto-bindings.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http) for code samples and instructions to try out output bindings.
|
|
||||||
|
|
||||||
### Interact with input bindings
|
|
||||||
|
|
||||||
```java
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/")
|
|
||||||
public class myClass {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(myClass);
|
|
||||||
@PostMapping(path = "/checkout")
|
|
||||||
public Mono<String> getCheckout(@RequestBody(required = false) byte[] body) {
|
|
||||||
return Mono.fromRunnable(() ->
|
|
||||||
log.info("Received Message: " + new String(body)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on input bindings, visit [How-To: Input bindings]({{< ref howto-triggers >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http) for code samples and instructions to try out input bindings.
|
|
||||||
|
|
||||||
### Retrieve secrets
|
|
||||||
|
|
||||||
```java
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
|
||||||
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
|
|
||||||
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on secrets visit [How-To: Retrieve secrets]({{< ref howto-secrets.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/secrets) for code samples and instructions to try out retrieving secrets
|
|
||||||
|
|
||||||
### Actors
|
|
||||||
An actor is an isolated, independent unit of compute and state with single-threaded execution. Dapr provides an actor implementation based on the [Virtual Actor pattern](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/), which provides a single-threaded programming model and where actors are garbage collected when not in use. With Dapr's implementation, you write your Dapr actors according to the Actor model, and Dapr leverages the scalability and reliability that the underlying platform provides.
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.actors.ActorMethod;
|
|
||||||
import io.dapr.actors.ActorType;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
@ActorType(name = "DemoActor")
|
|
||||||
public interface DemoActor {
|
|
||||||
|
|
||||||
void registerReminder();
|
|
||||||
|
|
||||||
@ActorMethod(name = "echo_message")
|
|
||||||
String say(String something);
|
|
||||||
|
|
||||||
void clock(String message);
|
|
||||||
|
|
||||||
@ActorMethod(returns = Integer.class)
|
|
||||||
Mono<Integer> incrementAndGet(int delta);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full guide on actors visit [How-To: Use virtual actors in Dapr]({{< ref howto-actors.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/actors) for code samples and instructions to try actors
|
|
||||||
|
|
||||||
### Get & Subscribe to application configurations
|
|
||||||
|
|
||||||
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import io.dapr.client.DaprPreviewClient;
|
|
||||||
import io.dapr.client.domain.ConfigurationItem;
|
|
||||||
import io.dapr.client.domain.GetConfigurationRequest;
|
|
||||||
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
|
||||||
// Get configuration for a single key
|
|
||||||
Mono<ConfigurationItem> item = client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY).block();
|
|
||||||
|
|
||||||
// Get configurations for multiple keys
|
|
||||||
Mono<Map<String, ConfigurationItem>> items =
|
|
||||||
client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2);
|
|
||||||
|
|
||||||
// Subscribe to configuration changes
|
|
||||||
Flux<SubscribeConfigurationResponse> outFlux = client.subscribeConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2);
|
|
||||||
outFlux.subscribe(configItems -> configItems.forEach(...));
|
|
||||||
|
|
||||||
// Unsubscribe from configuration changes
|
|
||||||
Mono<UnsubscribeConfigurationResponse> unsubscribe = client.unsubscribeConfiguration(SUBSCRIPTION_ID, CONFIG_STORE_NAME)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- For a full list of configuration operations visit [How-To: Manage configuration from a store]({{< ref howto-manage-configuration.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/configuration) for code samples and instructions to try out different configuration operations.
|
|
||||||
|
|
||||||
### Query saved state
|
|
||||||
|
|
||||||
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
|
||||||
|
|
||||||
```java
|
|
||||||
import io.dapr.client.DaprClient;
|
|
||||||
import io.dapr.client.DaprClientBuilder;
|
|
||||||
import io.dapr.client.DaprPreviewClient;
|
|
||||||
import io.dapr.client.domain.QueryStateItem;
|
|
||||||
import io.dapr.client.domain.QueryStateRequest;
|
|
||||||
import io.dapr.client.domain.QueryStateResponse;
|
|
||||||
import io.dapr.client.domain.query.Query;
|
|
||||||
import io.dapr.client.domain.query.Sorting;
|
|
||||||
import io.dapr.client.domain.query.filters.EqFilter;
|
|
||||||
|
|
||||||
try (DaprClient client = builder.build(); DaprPreviewClient previewClient = builder.buildPreviewClient()) {
|
|
||||||
String searchVal = args.length == 0 ? "searchValue" : args[0];
|
|
||||||
|
|
||||||
// Create JSON data
|
|
||||||
Listing first = new Listing();
|
|
||||||
first.setPropertyType("apartment");
|
|
||||||
first.setId("1000");
|
|
||||||
...
|
|
||||||
Listing second = new Listing();
|
|
||||||
second.setPropertyType("row-house");
|
|
||||||
second.setId("1002");
|
|
||||||
...
|
|
||||||
Listing third = new Listing();
|
|
||||||
third.setPropertyType("apartment");
|
|
||||||
third.setId("1003");
|
|
||||||
...
|
|
||||||
Listing fourth = new Listing();
|
|
||||||
fourth.setPropertyType("apartment");
|
|
||||||
fourth.setId("1001");
|
|
||||||
...
|
|
||||||
Map<String, String> meta = new HashMap<>();
|
|
||||||
meta.put("contentType", "application/json");
|
|
||||||
|
|
||||||
// Save state
|
|
||||||
SaveStateRequest request = new SaveStateRequest(STATE_STORE_NAME).setStates(
|
|
||||||
new State<>("1", first, null, meta, null),
|
|
||||||
new State<>("2", second, null, meta, null),
|
|
||||||
new State<>("3", third, null, meta, null),
|
|
||||||
new State<>("4", fourth, null, meta, null)
|
|
||||||
);
|
|
||||||
client.saveBulkState(request).block();
|
|
||||||
|
|
||||||
|
|
||||||
// Create query and query state request
|
|
||||||
|
|
||||||
Query query = new Query()
|
|
||||||
.setFilter(new EqFilter<>("propertyType", "apartment"))
|
|
||||||
.setSort(Arrays.asList(new Sorting("id", Sorting.Order.DESC)));
|
|
||||||
QueryStateRequest request = new QueryStateRequest(STATE_STORE_NAME)
|
|
||||||
.setQuery(query);
|
|
||||||
|
|
||||||
// Use preview client to call query state API
|
|
||||||
QueryStateResponse<MyData> result = previewClient.queryState(request, MyData.class).block();
|
|
||||||
|
|
||||||
// View Query state response
|
|
||||||
System.out.println("Found " + result.getResults().size() + " items.");
|
|
||||||
for (QueryStateItem<Listing> item : result.getResults()) {
|
|
||||||
System.out.println("Key: " + item.getKey());
|
|
||||||
System.out.println("Data: " + item.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
- For a full list of configuration operations visit [How-To: Query state]({{< ref howto-state-query-api.md >}}).
|
|
||||||
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/querystate) for complete code sample.
|
|
||||||
|
|
||||||
## Related links
|
|
||||||
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)
|
|
|
@ -0,0 +1,502 @@
|
||||||
|
---
|
||||||
|
type: docs
|
||||||
|
title: "Dapr Java SDK"
|
||||||
|
linkTitle: "Java"
|
||||||
|
weight: 2000
|
||||||
|
description: Java SDK packages for developing Dapr applications
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
[Complete initial setup and import the Java SDK into your project]({{< ref java-sdk-docs >}})
|
||||||
|
|
||||||
|
## Building blocks
|
||||||
|
|
||||||
|
The Java SDK allows you to interface with all of the [Dapr building blocks]({{< ref building-blocks >}}).
|
||||||
|
|
||||||
|
### Invoke a service
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
|
||||||
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
|
// invoke a 'GET' method (HTTP) skipping serialization: \say with a Mono<byte[]> return type
|
||||||
|
// for gRPC set HttpExtension.NONE parameters below
|
||||||
|
response = client.invokeMethod(SERVICE_TO_INVOKE, METHOD_TO_INVOKE, "{\"name\":\"World!\"}", HttpExtension.GET, byte[].class).block();
|
||||||
|
|
||||||
|
// invoke a 'POST' method (HTTP) skipping serialization: to \say with a Mono<byte[]> return type
|
||||||
|
response = client.invokeMethod(SERVICE_TO_INVOKE, METHOD_TO_INVOKE, "{\"id\":\"100\", \"FirstName\":\"Value\", \"LastName\":\"Value\"}", HttpExtension.POST, byte[].class).block();
|
||||||
|
|
||||||
|
System.out.println(new String(response));
|
||||||
|
|
||||||
|
// invoke a 'POST' method (HTTP) with serialization: \employees with a Mono<Employee> return type
|
||||||
|
Employee newEmployee = new Employee("Nigel", "Guitarist");
|
||||||
|
Employee employeeResponse = client.invokeMethod(SERVICE_TO_INVOKE, "employees", newEmployee, HttpExtension.POST, Employee.class).block();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on service invocation visit [How-To: Invoke a service]({{< ref howto-invoke-discover-services.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/invoke) for code samples and instructions to try out service invocation
|
||||||
|
|
||||||
|
### Save & get application state
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.domain.State;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
|
// Save state
|
||||||
|
client.saveState(STATE_STORE_NAME, FIRST_KEY_NAME, myClass).block();
|
||||||
|
|
||||||
|
// Get state
|
||||||
|
State<MyClass> retrievedMessage = client.getState(STATE_STORE_NAME, FIRST_KEY_NAME, MyClass.class).block();
|
||||||
|
|
||||||
|
// Delete state
|
||||||
|
client.deleteState(STATE_STORE_NAME, FIRST_KEY_NAME).block();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full list of state operations visit [How-To: Get & save state]({{< ref howto-get-save-state.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/state) for code samples and instructions to try out state management
|
||||||
|
|
||||||
|
### Publish & subscribe to messages
|
||||||
|
|
||||||
|
##### Publish messages
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.domain.Metadata;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
|
||||||
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
|
client.publishEvent(PUBSUB_NAME, TOPIC_NAME, message, singletonMap(Metadata.TTL_IN_SECONDS, MESSAGE_TTL_IN_SECONDS)).block();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Subscribe to messages
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.dapr.Topic;
|
||||||
|
import io.dapr.client.domain.BulkSubscribeAppResponse;
|
||||||
|
import io.dapr.client.domain.BulkSubscribeAppResponseEntry;
|
||||||
|
import io.dapr.client.domain.BulkSubscribeAppResponseStatus;
|
||||||
|
import io.dapr.client.domain.BulkSubscribeMessage;
|
||||||
|
import io.dapr.client.domain.BulkSubscribeMessageEntry;
|
||||||
|
import io.dapr.client.domain.CloudEvent;
|
||||||
|
import io.dapr.springboot.annotations.BulkSubscribe;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class SubscriberController {
|
||||||
|
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
@Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}")
|
||||||
|
@PostMapping(path = "/testingtopic")
|
||||||
|
public Mono<Void> handleMessage(@RequestBody(required = false) CloudEvent<?> cloudEvent) {
|
||||||
|
return Mono.fromRunnable(() -> {
|
||||||
|
try {
|
||||||
|
System.out.println("Subscriber got: " + cloudEvent.getData());
|
||||||
|
System.out.println("Subscriber got: " + OBJECT_MAPPER.writeValueAsString(cloudEvent));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Topic(name = "testingtopic", pubsubName = "${myAppProperty:messagebus}",
|
||||||
|
rule = @Rule(match = "event.type == 'myevent.v2'", priority = 1))
|
||||||
|
@PostMapping(path = "/testingtopicV2")
|
||||||
|
public Mono<Void> handleMessageV2(@RequestBody(required = false) CloudEvent envelope) {
|
||||||
|
return Mono.fromRunnable(() -> {
|
||||||
|
try {
|
||||||
|
System.out.println("Subscriber got: " + cloudEvent.getData());
|
||||||
|
System.out.println("Subscriber got: " + OBJECT_MAPPER.writeValueAsString(cloudEvent));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@BulkSubscribe()
|
||||||
|
@Topic(name = "testingtopicbulk", pubsubName = "${myAppProperty:messagebus}")
|
||||||
|
@PostMapping(path = "/testingtopicbulk")
|
||||||
|
public Mono<BulkSubscribeAppResponse> handleBulkMessage(
|
||||||
|
@RequestBody(required = false) BulkSubscribeMessage<CloudEvent<String>> bulkMessage) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
if (bulkMessage.getEntries().size() == 0) {
|
||||||
|
return new BulkSubscribeAppResponse(new ArrayList<BulkSubscribeAppResponseEntry>());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Bulk Subscriber received " + bulkMessage.getEntries().size() + " messages.");
|
||||||
|
|
||||||
|
List<BulkSubscribeAppResponseEntry> entries = new ArrayList<BulkSubscribeAppResponseEntry>();
|
||||||
|
for (BulkSubscribeMessageEntry<?> entry : bulkMessage.getEntries()) {
|
||||||
|
try {
|
||||||
|
System.out.printf("Bulk Subscriber message has entry ID: %s\n", entry.getEntryId());
|
||||||
|
CloudEvent<?> cloudEvent = (CloudEvent<?>) entry.getEvent();
|
||||||
|
System.out.printf("Bulk Subscriber got: %s\n", cloudEvent.getData());
|
||||||
|
entries.add(new BulkSubscribeAppResponseEntry(entry.getEntryId(), BulkSubscribeAppResponseStatus.SUCCESS));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
entries.add(new BulkSubscribeAppResponseEntry(entry.getEntryId(), BulkSubscribeAppResponseStatus.RETRY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new BulkSubscribeAppResponse(entries);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Bulk Publish Messages
|
||||||
|
> Note: API is in Alpha stage
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.BulkPublishResponse;
|
||||||
|
import io.dapr.client.domain.BulkPublishResponseFailedEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
class Solution {
|
||||||
|
public void publishMessages() {
|
||||||
|
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
||||||
|
// Create a list of messages to publish
|
||||||
|
List<String> messages = new ArrayList<>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish list of messages using the bulk publish API
|
||||||
|
BulkPublishResponse<String> res = client.publishEvents(PUBSUB_NAME, TOPIC_NAME, "text/plain", messages).block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on publishing messages and subscribing to a topic [How-To: Publish & subscribe]({{< ref howto-publish-subscribe.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/pubsub/http) for code samples and instructions to try out pub/sub
|
||||||
|
|
||||||
|
### Interact with output bindings
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
|
||||||
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
|
// sending a class with message; BINDING_OPERATION="create"
|
||||||
|
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass).block();
|
||||||
|
|
||||||
|
// sending a plain string
|
||||||
|
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message).block();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on output bindings visit [How-To: Output bindings]({{< ref howto-bindings.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http) for code samples and instructions to try out output bindings.
|
||||||
|
|
||||||
|
### Interact with input bindings
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/")
|
||||||
|
public class myClass {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(myClass);
|
||||||
|
@PostMapping(path = "/checkout")
|
||||||
|
public Mono<String> getCheckout(@RequestBody(required = false) byte[] body) {
|
||||||
|
return Mono.fromRunnable(() ->
|
||||||
|
log.info("Received Message: " + new String(body)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on input bindings, visit [How-To: Input bindings]({{< ref howto-triggers >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http) for code samples and instructions to try out input bindings.
|
||||||
|
|
||||||
|
### Retrieve secrets
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
try (DaprClient client = (new DaprClientBuilder()).build()) {
|
||||||
|
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
|
||||||
|
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on secrets visit [How-To: Retrieve secrets]({{< ref howto-secrets.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/secrets) for code samples and instructions to try out retrieving secrets
|
||||||
|
|
||||||
|
### Actors
|
||||||
|
An actor is an isolated, independent unit of compute and state with single-threaded execution. Dapr provides an actor implementation based on the [Virtual Actor pattern](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/), which provides a single-threaded programming model and where actors are garbage collected when not in use. With Dapr's implementaiton, you write your Dapr actors according to the Actor model, and Dapr leverages the scalability and reliability that the underlying platform provides.
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.actors.ActorMethod;
|
||||||
|
import io.dapr.actors.ActorType;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@ActorType(name = "DemoActor")
|
||||||
|
public interface DemoActor {
|
||||||
|
|
||||||
|
void registerReminder();
|
||||||
|
|
||||||
|
@ActorMethod(name = "echo_message")
|
||||||
|
String say(String something);
|
||||||
|
|
||||||
|
void clock(String message);
|
||||||
|
|
||||||
|
@ActorMethod(returns = Integer.class)
|
||||||
|
Mono<Integer> incrementAndGet(int delta);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on actors visit [How-To: Use virtual actors in Dapr]({{< ref howto-actors.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/actors) for code samples and instructions to try actors
|
||||||
|
|
||||||
|
### Get & Subscribe to application configurations
|
||||||
|
|
||||||
|
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.ConfigurationItem;
|
||||||
|
import io.dapr.client.domain.GetConfigurationRequest;
|
||||||
|
import io.dapr.client.domain.SubscribeConfigurationRequest;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
||||||
|
// Get configuration for a single key
|
||||||
|
Mono<ConfigurationItem> item = client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY).block();
|
||||||
|
|
||||||
|
// Get configurations for multiple keys
|
||||||
|
Mono<Map<String, ConfigurationItem>> items =
|
||||||
|
client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2);
|
||||||
|
|
||||||
|
// Subscribe to configuration changes
|
||||||
|
Flux<SubscribeConfigurationResponse> outFlux = client.subscribeConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2);
|
||||||
|
outFlux.subscribe(configItems -> configItems.forEach(...));
|
||||||
|
|
||||||
|
// Unsubscribe from configuration changes
|
||||||
|
Mono<UnsubscribeConfigurationResponse> unsubscribe = client.unsubscribeConfiguration(SUBSCRIPTION_ID, CONFIG_STORE_NAME)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full list of configuration operations visit [How-To: Manage configuration from a store]({{< ref howto-manage-configuration.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/configuration) for code samples and instructions to try out different configuration operations.
|
||||||
|
|
||||||
|
### Query saved state
|
||||||
|
|
||||||
|
> Note this is a preview API and thus will only be accessible via the DaprPreviewClient interface and not the normal DaprClient interface
|
||||||
|
|
||||||
|
```java
|
||||||
|
import io.dapr.client.DaprClient;
|
||||||
|
import io.dapr.client.DaprClientBuilder;
|
||||||
|
import io.dapr.client.DaprPreviewClient;
|
||||||
|
import io.dapr.client.domain.QueryStateItem;
|
||||||
|
import io.dapr.client.domain.QueryStateRequest;
|
||||||
|
import io.dapr.client.domain.QueryStateResponse;
|
||||||
|
import io.dapr.client.domain.query.Query;
|
||||||
|
import io.dapr.client.domain.query.Sorting;
|
||||||
|
import io.dapr.client.domain.query.filters.EqFilter;
|
||||||
|
|
||||||
|
try (DaprClient client = builder.build(); DaprPreviewClient previewClient = builder.buildPreviewClient()) {
|
||||||
|
String searchVal = args.length == 0 ? "searchValue" : args[0];
|
||||||
|
|
||||||
|
// Create JSON data
|
||||||
|
Listing first = new Listing();
|
||||||
|
first.setPropertyType("apartment");
|
||||||
|
first.setId("1000");
|
||||||
|
...
|
||||||
|
Listing second = new Listing();
|
||||||
|
second.setPropertyType("row-house");
|
||||||
|
second.setId("1002");
|
||||||
|
...
|
||||||
|
Listing third = new Listing();
|
||||||
|
third.setPropertyType("apartment");
|
||||||
|
third.setId("1003");
|
||||||
|
...
|
||||||
|
Listing fourth = new Listing();
|
||||||
|
fourth.setPropertyType("apartment");
|
||||||
|
fourth.setId("1001");
|
||||||
|
...
|
||||||
|
Map<String, String> meta = new HashMap<>();
|
||||||
|
meta.put("contentType", "application/json");
|
||||||
|
|
||||||
|
// Save state
|
||||||
|
SaveStateRequest request = new SaveStateRequest(STATE_STORE_NAME).setStates(
|
||||||
|
new State<>("1", first, null, meta, null),
|
||||||
|
new State<>("2", second, null, meta, null),
|
||||||
|
new State<>("3", third, null, meta, null),
|
||||||
|
new State<>("4", fourth, null, meta, null)
|
||||||
|
);
|
||||||
|
client.saveBulkState(request).block();
|
||||||
|
|
||||||
|
|
||||||
|
// Create query and query state request
|
||||||
|
|
||||||
|
Query query = new Query()
|
||||||
|
.setFilter(new EqFilter<>("propertyType", "apartment"))
|
||||||
|
.setSort(Arrays.asList(new Sorting("id", Sorting.Order.DESC)));
|
||||||
|
QueryStateRequest request = new QueryStateRequest(STATE_STORE_NAME)
|
||||||
|
.setQuery(query);
|
||||||
|
|
||||||
|
// Use preview client to call query state API
|
||||||
|
QueryStateResponse<MyData> result = previewClient.queryState(request, MyData.class).block();
|
||||||
|
|
||||||
|
// View Query state response
|
||||||
|
System.out.println("Found " + result.getResults().size() + " items.");
|
||||||
|
for (QueryStateItem<Listing> item : result.getResults()) {
|
||||||
|
System.out.println("Key: " + item.getKey());
|
||||||
|
System.out.println("Data: " + item.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- For a full how-to on query state, visit [How-To: Query state]({{< ref howto-state-query-api.md >}}).
|
||||||
|
- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/querystate) for complete code sample.
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
> Dapr Workflow is currently in beta state.
|
||||||
|
|
||||||
|
```java
|
||||||
|
package io.dapr.examples.workflows;
|
||||||
|
|
||||||
|
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||||
|
import io.dapr.workflows.client.WorkflowInstanceStatus;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For setup instructions, see the README.
|
||||||
|
*/
|
||||||
|
public class DemoWorkflowClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main method.
|
||||||
|
*
|
||||||
|
* @param args Input arguments (unused).
|
||||||
|
* @throws InterruptedException If program has been interrupted.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws InterruptedException {
|
||||||
|
DaprWorkflowClient client = new DaprWorkflowClient();
|
||||||
|
|
||||||
|
try (client) {
|
||||||
|
String separatorStr = "*******";
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
String instanceId = client.scheduleNewWorkflow(DemoWorkflow.class, "input data");
|
||||||
|
System.out.printf("Started new workflow instance with random ID: %s%n", instanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**GetInstanceMetadata:Running Workflow**");
|
||||||
|
WorkflowInstanceStatus workflowMetadata = client.getInstanceState(instanceId, true);
|
||||||
|
System.out.printf("Result: %s%n", workflowMetadata);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**WaitForInstanceStart**");
|
||||||
|
try {
|
||||||
|
WorkflowInstanceStatus waitForInstanceStartResult =
|
||||||
|
client.waitForInstanceStart(instanceId, Duration.ofSeconds(60), true);
|
||||||
|
System.out.printf("Result: %s%n", waitForInstanceStartResult);
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
System.out.printf("waitForInstanceStart has an exception:%s%n", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**SendExternalMessage**");
|
||||||
|
client.raiseEvent(instanceId, "TestEvent", "TestEventPayload");
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("** Registering parallel Events to be captured by allOf(t1,t2,t3) **");
|
||||||
|
client.raiseEvent(instanceId, "event1", "TestEvent 1 Payload");
|
||||||
|
client.raiseEvent(instanceId, "event2", "TestEvent 2 Payload");
|
||||||
|
client.raiseEvent(instanceId, "event3", "TestEvent 3 Payload");
|
||||||
|
System.out.printf("Events raised for workflow with instanceId: %s\n", instanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("** Registering Event to be captured by anyOf(t1,t2,t3) **");
|
||||||
|
client.raiseEvent(instanceId, "e2", "event 2 Payload");
|
||||||
|
System.out.printf("Event raised for workflow with instanceId: %s\n", instanceId);
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**WaitForInstanceCompletion**");
|
||||||
|
try {
|
||||||
|
WorkflowInstanceStatus waitForInstanceCompletionResult =
|
||||||
|
client.waitForInstanceCompletion(instanceId, Duration.ofSeconds(60), true);
|
||||||
|
System.out.printf("Result: %s%n", waitForInstanceCompletionResult);
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
System.out.printf("waitForInstanceCompletion has an exception:%s%n", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**purgeInstance**");
|
||||||
|
boolean purgeResult = client.purgeInstance(instanceId);
|
||||||
|
System.out.printf("purgeResult: %s%n", purgeResult);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**raiseEvent**");
|
||||||
|
|
||||||
|
String eventInstanceId = client.scheduleNewWorkflow(DemoWorkflow.class);
|
||||||
|
System.out.printf("Started new workflow instance with random ID: %s%n", eventInstanceId);
|
||||||
|
client.raiseEvent(eventInstanceId, "TestException", null);
|
||||||
|
System.out.printf("Event raised for workflow with instanceId: %s\n", eventInstanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
String instanceToTerminateId = "terminateMe";
|
||||||
|
client.scheduleNewWorkflow(DemoWorkflow.class, null, instanceToTerminateId);
|
||||||
|
System.out.printf("Started new workflow instance with specified ID: %s%n", instanceToTerminateId);
|
||||||
|
|
||||||
|
TimeUnit.SECONDS.sleep(5);
|
||||||
|
System.out.println("Terminate this workflow instance manually before the timeout is reached");
|
||||||
|
client.terminateWorkflow(instanceToTerminateId, null);
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
|
||||||
|
String restartingInstanceId = "restarting";
|
||||||
|
client.scheduleNewWorkflow(DemoWorkflow.class, null, restartingInstanceId);
|
||||||
|
System.out.printf("Started new workflow instance with ID: %s%n", restartingInstanceId);
|
||||||
|
System.out.println("Sleeping 30 seconds to restart the workflow");
|
||||||
|
TimeUnit.SECONDS.sleep(30);
|
||||||
|
|
||||||
|
System.out.println("**SendExternalMessage: RestartEvent**");
|
||||||
|
client.raiseEvent(restartingInstanceId, "RestartEvent", "RestartEventPayload");
|
||||||
|
|
||||||
|
System.out.println("Sleeping 30 seconds to terminate the eternal workflow");
|
||||||
|
TimeUnit.SECONDS.sleep(30);
|
||||||
|
client.terminateWorkflow(restartingInstanceId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Exiting DemoWorkflowClient.");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- For a full guide on workflows, visit:
|
||||||
|
- [How-To: Author workflows]({{< ref howto-author-workflow.md >}}).
|
||||||
|
- [How-To: Manage workflows]({{< ref howto-manage-workflow.md >}}).
|
||||||
|
- [Learn more about how to use workflows with the Java SDK]({{< ref java-workflow.md >}}).
|
||||||
|
|
||||||
|
## Related links
|
||||||
|
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
type: docs
|
||||||
|
title: "Workflow"
|
||||||
|
linkTitle: "Workflow"
|
||||||
|
weight: 3000
|
||||||
|
description: How to get up and running with the Dapr Workflow extension
|
||||||
|
---
|
|
@ -0,0 +1,254 @@
|
||||||
|
---
|
||||||
|
type: docs
|
||||||
|
title: "How to: Author and manage Dapr Workflow in the Java SDK"
|
||||||
|
linkTitle: "How to: Author and manage workflows"
|
||||||
|
weight: 20000
|
||||||
|
description: How to get up and running with workflows using the Dapr Java SDK
|
||||||
|
---
|
||||||
|
|
||||||
|
{{% alert title="Note" color="primary" %}}
|
||||||
|
Dapr Workflow is currently in beta. [See known limitations for {{% dapr-latest-version cli="true" %}}]({{< ref "workflow-overview.md#limitations" >}}).
|
||||||
|
{{% /alert %}}
|
||||||
|
|
||||||
|
|
||||||
|
Let’s create a Dapr workflow and invoke it using the console. With the [provided workflow example](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/workflows), you will:
|
||||||
|
|
||||||
|
- Execute the workflow instance using the [Java workflow worker](https://github.com/dapr/java-sdk/blob/master/examples/src/main/java/io/dapr/examples/workflows/DemoWorkflowWorker.java)
|
||||||
|
- Utilize the Java workflow client and API calls to [start and terminate workflow instances](https://github.com/dapr/java-sdk/blob/master/examples/src/main/java/io/dapr/examples/workflows/DemoWorkflowClient.java)
|
||||||
|
|
||||||
|
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.
|
||||||
|
<!-- IGNORE_LINKS -->
|
||||||
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
<!-- END_IGNORE -->
|
||||||
|
- Verify you're using the latest proto bindings
|
||||||
|
|
||||||
|
## Set up the environment
|
||||||
|
|
||||||
|
Clone the Java SDK repo 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 this workflow sample with the Dapr Java SDK.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean install
|
||||||
|
```
|
||||||
|
|
||||||
|
From the Java SDK root directory, navigate to the Dapr Workflow example.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run the `DemoWorkflowWorker`
|
||||||
|
|
||||||
|
The `DemoWorkflowWorker` class registers an implementation of `DemoWorkflow` in Dapr's workflow runtime engine. In the `DemoWorkflowWorker.java` file, you can find the `DemoWorkflowWorker` class and the `main` method:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class DemoWorkflowWorker {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
// Register the Workflow with the runtime.
|
||||||
|
WorkflowRuntime.getInstance().registerWorkflow(DemoWorkflow.class);
|
||||||
|
System.out.println("Start workflow runtime");
|
||||||
|
WorkflowRuntime.getInstance().startAndBlock();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the code above:
|
||||||
|
- `WorkflowRuntime.getInstance().registerWorkflow()` registers `DemoWorkflow` as a workflow in the Dapr Workflow runtime.
|
||||||
|
- `WorkflowRuntime.getInstance().start()` builds and starts the engine within the Dapr Workflow runtime.
|
||||||
|
|
||||||
|
In the terminal, execute the following command to kick off the `DemoWorkflowWorker`:
|
||||||
|
|
||||||
|
```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.DemoWorkflowWorker
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**
|
||||||
|
|
||||||
|
```
|
||||||
|
You're up and running! Both Dapr and your app logs will appear here.
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
== APP == Start workflow runtime
|
||||||
|
== APP == Sep 13, 2023 9:02:03 AM com.microsoft.durabletask.DurableTaskGrpcWorker startAndBlock
|
||||||
|
== APP == INFO: Durable Task worker is connecting to sidecar at 127.0.0.1:50001.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run the `DemoWorkflowClient
|
||||||
|
|
||||||
|
The `DemoWorkflowClient` starts instances of workflows that have been registered with Dapr.
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class DemoWorkflowClient {
|
||||||
|
|
||||||
|
// ...
|
||||||
|
public static void main(String[] args) throws InterruptedException {
|
||||||
|
DaprWorkflowClient client = new DaprWorkflowClient();
|
||||||
|
|
||||||
|
try (client) {
|
||||||
|
String separatorStr = "*******";
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
String instanceId = client.scheduleNewWorkflow(DemoWorkflow.class, "input data");
|
||||||
|
System.out.printf("Started new workflow instance with random ID: %s%n", instanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**GetInstanceMetadata:Running Workflow**");
|
||||||
|
WorkflowInstanceStatus workflowMetadata = client.getInstanceState(instanceId, true);
|
||||||
|
System.out.printf("Result: %s%n", workflowMetadata);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**WaitForInstanceStart**");
|
||||||
|
try {
|
||||||
|
WorkflowInstanceStatus waitForInstanceStartResult =
|
||||||
|
client.waitForInstanceStart(instanceId, Duration.ofSeconds(60), true);
|
||||||
|
System.out.printf("Result: %s%n", waitForInstanceStartResult);
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
System.out.printf("waitForInstanceStart has an exception:%s%n", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**SendExternalMessage**");
|
||||||
|
client.raiseEvent(instanceId, "TestEvent", "TestEventPayload");
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("** Registering parallel Events to be captured by allOf(t1,t2,t3) **");
|
||||||
|
client.raiseEvent(instanceId, "event1", "TestEvent 1 Payload");
|
||||||
|
client.raiseEvent(instanceId, "event2", "TestEvent 2 Payload");
|
||||||
|
client.raiseEvent(instanceId, "event3", "TestEvent 3 Payload");
|
||||||
|
System.out.printf("Events raised for workflow with instanceId: %s\n", instanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("** Registering Event to be captured by anyOf(t1,t2,t3) **");
|
||||||
|
client.raiseEvent(instanceId, "e2", "event 2 Payload");
|
||||||
|
System.out.printf("Event raised for workflow with instanceId: %s\n", instanceId);
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**WaitForInstanceCompletion**");
|
||||||
|
try {
|
||||||
|
WorkflowInstanceStatus waitForInstanceCompletionResult =
|
||||||
|
client.waitForInstanceCompletion(instanceId, Duration.ofSeconds(60), true);
|
||||||
|
System.out.printf("Result: %s%n", waitForInstanceCompletionResult);
|
||||||
|
} catch (TimeoutException ex) {
|
||||||
|
System.out.printf("waitForInstanceCompletion has an exception:%s%n", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**purgeInstance**");
|
||||||
|
boolean purgeResult = client.purgeInstance(instanceId);
|
||||||
|
System.out.printf("purgeResult: %s%n", purgeResult);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
System.out.println("**raiseEvent**");
|
||||||
|
|
||||||
|
String eventInstanceId = client.scheduleNewWorkflow(DemoWorkflow.class);
|
||||||
|
System.out.printf("Started new workflow instance with random ID: %s%n", eventInstanceId);
|
||||||
|
client.raiseEvent(eventInstanceId, "TestException", null);
|
||||||
|
System.out.printf("Event raised for workflow with instanceId: %s\n", eventInstanceId);
|
||||||
|
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
String instanceToTerminateId = "terminateMe";
|
||||||
|
client.scheduleNewWorkflow(DemoWorkflow.class, null, instanceToTerminateId);
|
||||||
|
System.out.printf("Started new workflow instance with specified ID: %s%n", instanceToTerminateId);
|
||||||
|
|
||||||
|
TimeUnit.SECONDS.sleep(5);
|
||||||
|
System.out.println("Terminate this workflow instance manually before the timeout is reached");
|
||||||
|
client.terminateWorkflow(instanceToTerminateId, null);
|
||||||
|
System.out.println(separatorStr);
|
||||||
|
|
||||||
|
String restartingInstanceId = "restarting";
|
||||||
|
client.scheduleNewWorkflow(DemoWorkflow.class, null, restartingInstanceId);
|
||||||
|
System.out.printf("Started new workflow instance with ID: %s%n", restartingInstanceId);
|
||||||
|
System.out.println("Sleeping 30 seconds to restart the workflow");
|
||||||
|
TimeUnit.SECONDS.sleep(30);
|
||||||
|
|
||||||
|
System.out.println("**SendExternalMessage: RestartEvent**");
|
||||||
|
client.raiseEvent(restartingInstanceId, "RestartEvent", "RestartEventPayload");
|
||||||
|
|
||||||
|
System.out.println("Sleeping 30 seconds to terminate the eternal workflow");
|
||||||
|
TimeUnit.SECONDS.sleep(30);
|
||||||
|
client.terminateWorkflow(restartingInstanceId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Exiting DemoWorkflowClient.");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In a second terminal window, start the workflow by running the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.workflows.DemoWorkflowClient
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**
|
||||||
|
|
||||||
|
```
|
||||||
|
*******
|
||||||
|
Started new workflow instance with random ID: 0b4cc0d5-413a-4c1c-816a-a71fa24740d4
|
||||||
|
*******
|
||||||
|
**GetInstanceMetadata:Running Workflow**
|
||||||
|
Result: [Name: 'io.dapr.examples.workflows.DemoWorkflow', ID: '0b4cc0d5-413a-4c1c-816a-a71fa24740d4', RuntimeStatus: RUNNING, CreatedAt: 2023-09-13T13:02:30.547Z, LastUpdatedAt: 2023-09-13T13:02:30.699Z, Input: '"input data"', Output: '']
|
||||||
|
*******
|
||||||
|
**WaitForInstanceStart**
|
||||||
|
Result: [Name: 'io.dapr.examples.workflows.DemoWorkflow', ID: '0b4cc0d5-413a-4c1c-816a-a71fa24740d4', RuntimeStatus: RUNNING, CreatedAt: 2023-09-13T13:02:30.547Z, LastUpdatedAt: 2023-09-13T13:02:30.699Z, Input: '"input data"', Output: '']
|
||||||
|
*******
|
||||||
|
**SendExternalMessage**
|
||||||
|
*******
|
||||||
|
** Registering parallel Events to be captured by allOf(t1,t2,t3) **
|
||||||
|
Events raised for workflow with instanceId: 0b4cc0d5-413a-4c1c-816a-a71fa24740d4
|
||||||
|
*******
|
||||||
|
** Registering Event to be captured by anyOf(t1,t2,t3) **
|
||||||
|
Event raised for workflow with instanceId: 0b4cc0d5-413a-4c1c-816a-a71fa24740d4
|
||||||
|
*******
|
||||||
|
**WaitForInstanceCompletion**
|
||||||
|
Result: [Name: 'io.dapr.examples.workflows.DemoWorkflow', ID: '0b4cc0d5-413a-4c1c-816a-a71fa24740d4', RuntimeStatus: FAILED, CreatedAt: 2023-09-13T13:02:30.547Z, LastUpdatedAt: 2023-09-13T13:02:55.054Z, Input: '"input data"', Output: '']
|
||||||
|
*******
|
||||||
|
**purgeInstance**
|
||||||
|
purgeResult: true
|
||||||
|
*******
|
||||||
|
**raiseEvent**
|
||||||
|
Started new workflow instance with random ID: 7707d141-ebd0-4e54-816e-703cb7a52747
|
||||||
|
Event raised for workflow with instanceId: 7707d141-ebd0-4e54-816e-703cb7a52747
|
||||||
|
*******
|
||||||
|
Started new workflow instance with specified ID: terminateMe
|
||||||
|
Terminate this workflow instance manually before the timeout is reached
|
||||||
|
*******
|
||||||
|
Started new workflow instance with ID: restarting
|
||||||
|
Sleeping 30 seconds to restart the workflow
|
||||||
|
**SendExternalMessage: RestartEvent**
|
||||||
|
Sleeping 30 seconds to terminate the eternal workflow
|
||||||
|
Exiting DemoWorkflowClient.
|
||||||
|
```
|
||||||
|
|
||||||
|
## What happened?
|
||||||
|
|
||||||
|
1. When you ran `dapr run`, the workflow worker registered the workflow (`DemoWorkflow`) and its actvities to the Dapr Workflow engine.
|
||||||
|
1. When you ran `java`, the workflow client started the workflow instance with the following activities. You can follow along with the output in the terminal where you ran `dapr run`.
|
||||||
|
1. The workflow is started, raises three parallel tasks, and waits for them to complete.
|
||||||
|
1. The workflow client calls the activity and sends the "Hello Activity" message to the console.
|
||||||
|
1. The workflow times out and is purged.
|
||||||
|
1. The workflow client starts a new workflow instance with a random ID, uses another workflow instance called `terminateMe` to terminate it, and restarts it with the workflow called `restarting`.
|
||||||
|
1. The worfklow client is then exited.
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
- [Learn more about Dapr workflow]({{< ref workflow >}})
|
||||||
|
- [Workflow API reference]({{< ref workflow_api.md >}})
|
Loading…
Reference in New Issue