diff --git a/daprdocs/README.md b/daprdocs/README.md deleted file mode 100644 index 1fe0b1234b..0000000000 --- a/daprdocs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Dapr Java SDK documentation - -This page covers how the documentation is structured for the Dapr Java SDK - -## Dapr Docs - -All Dapr documentation is hosted at [docs.dapr.io](https://docs.dapr.io), including the docs for the [Java SDK](https://docs.dapr.io/developing-applications/sdks/java/). Head over there if you want to read the docs. - -### Java SDK docs source - -Although the docs site code and content is in the [docs repo](https://github.com/dapr/docs), the Java SDK content and images are within the `content` and `static` directories, respectively. - -This allows separation of roles and expertise between maintainers, and makes it easy to find the docs files you are looking for. - -## Writing Java SDK docs - -To get up and running to write Java SDK docs, visit the [docs repo](https://github.com/dapr/docs) to initialize your environment. It will clone both the docs repo and this repo, so you can make changes and see it rendered within the site instantly, as well as commit and PR into this repo. - -Make sure to read the [docs contributing guide](https://docs.dapr.io/contributing/contributing-docs/) for information on style/semantics/etc. - -## Docs architecture - -The docs site is built on [Hugo](https://gohugo.io), which lives in the docs repo. This repo is setup as a git submodule so that when the repo is cloned and initialized, the java-sdk repo, along with the docs, are cloned as well. - -Then, in the Hugo configuration file, the `daprdocs/content` and `daprdocs/static` directories are redirected to the `daprdocs/developing-applications/sdks/java` and `static/java` directories, respectively. Thus, all the content within this repo is folded into the main docs site. \ No newline at end of file diff --git a/daprdocs/content/en/java-sdk-contributing/java-contributing.md b/daprdocs/content/en/java-sdk-contributing/java-contributing.md deleted file mode 100644 index 03ba6d4e51..0000000000 --- a/daprdocs/content/en/java-sdk-contributing/java-contributing.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -type: docs -title: "Contributing to the Java SDK" -linkTitle: "Java SDK" -weight: 3000 -description: Guidelines for contributing to the Dapr Java SDK ---- - -When contributing to the [Java SDK](https://github.com/dapr/java-sdk) the following rules and best-practices should be followed. - -## Examples - -The `examples` directory contains code samples for users to run to try out specific functionality of the various Java SDK packages and extensions. When writing new and updated samples keep in mind: - -- All examples should be runnable on Windows, Linux, and MacOS. While Java code is consistent among operating systems, any pre/post example commands should provide options through [tabpane]({{% ref "contributing-docs.md#tabbed-content" %}}) -- Contain steps to download/install any required pre-requisites. Someone coming in with a fresh OS install should be able to start on the example and complete it without an error. Links to external download pages are fine. - -## Docs - -The `daprdocs` directory contains the markdown files that are rendered into the [Dapr Docs](https://docs.dapr.io) website. When the documentation website is built, this repo is cloned and configured so that its contents are rendered with the docs content. When writing docs, keep in mind: - - - All rules in the [docs guide]({{% ref contributing-docs.md %}}) should be followed in addition to these. - - All files and directories should be prefixed with `java-` to ensure all file/directory names are globally unique across all Dapr documentation. - -## Github Dapr Bot Commands - -Checkout the [daprbot documentation](https://docs.dapr.io/contributing/daprbot/) for Github commands you can run in this repo for common tasks. For example, you can run the `/assign` (as a comment on an issue) to assign the issue to yourself. diff --git a/daprdocs/content/en/java-sdk-docs/_index.md b/daprdocs/content/en/java-sdk-docs/_index.md deleted file mode 100644 index d640101bc1..0000000000 --- a/daprdocs/content/en/java-sdk-docs/_index.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -type: docs -title: "Dapr Java SDK" -linkTitle: "Java" -weight: 1000 -description: Java SDK packages for developing Dapr applications -cascade: - github_repo: https://github.com/dapr/java-sdk - github_subdir: daprdocs/content/en/java-sdk-docs - path_base_for_github_subdir: content/en/developing-applications/sdks/java/ - github_branch: master ---- - -Dapr offers a variety of packages to help with the development of Java applications. Using them you can create Java clients, servers, and virtual actors with Dapr. - -## Prerequisites - -- [Dapr CLI]({{% ref install-dapr-cli.md %}}) installed -- Initialized [Dapr environment]({{% ref install-dapr-selfhost.md %}}) -- JDK 11 or above - the published jars are compatible with Java 8: - - [AdoptOpenJDK 11 - LTS](https://adoptopenjdk.net/) - - [Oracle's JDK 15](https://www.oracle.com/java/technologies/javase-downloads.html) - - [Oracle's JDK 11 - LTS](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) - - [OpenJDK](https://openjdk.java.net/) -- Install one of the following build tools for Java: - - [Maven 3.x](https://maven.apache.org/install.html) - - [Gradle 6.x](https://gradle.org/install/) - -## Import Dapr's Java SDK - -Next, import the Java SDK packages to get started. Select your preferred build tool to learn how to import. - -{{< tabpane text=true >}} - -{{% tab header="Maven" %}} - - -For a Maven project, add the following to your `pom.xml` file: - -```xml - - ... - - ... - - - io.dapr - dapr-sdk - 1.16.0 - - - - io.dapr - dapr-sdk-actors - 1.16.0 - - - - io.dapr - dapr-sdk-springboot - 1.16.0 - - ... - - ... - -``` -{{% /tab %}} - -{{% tab header="Gradle" %}} - - -For a Gradle project, add the following to your `build.gradle` file: - -```java -dependencies { -... - // Dapr's core SDK with all features, except Actors. - compile('io.dapr:dapr-sdk:1.16.0') - // Dapr's SDK for Actors (optional). - compile('io.dapr:dapr-sdk-actors:1.16.0') - // Dapr's SDK integration with SpringBoot (optional). - compile('io.dapr:dapr-sdk-springboot:1.16.0') -} -``` - -{{% /tab %}} - -{{< /tabpane >}} - -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 - - com.squareup.okhttp3 - okhttp - 1.16.0 - -``` - -## Try it out - -Put the Dapr Java SDK to the test. Walk through the Java quickstarts and tutorials to see Dapr in action: - -| 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 -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. - -## Available packages - -
-
-
-
Client
-

Create Java clients that interact with a Dapr sidecar and other Dapr applications.

- -
-
-
-
-
Workflow
-

Create and manage workflows that work with other Dapr APIs in Java.

- -
-
-
diff --git a/daprdocs/content/en/java-sdk-docs/java-ai/_index.md b/daprdocs/content/en/java-sdk-docs/java-ai/_index.md deleted file mode 100644 index 904edfc111..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-ai/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -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. ---- \ No newline at end of file diff --git a/daprdocs/content/en/java-sdk-docs/java-ai/java-ai-howto.md b/daprdocs/content/en/java-sdk-docs/java-ai/java-ai-howto.md deleted file mode 100644 index 39970d5218..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-ai/java-ai-howto.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -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 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 -``` - -As shown in the output, the number sent to the API is obfuscated and returned in the form of . -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 %}}) \ No newline at end of file diff --git a/daprdocs/content/en/java-sdk-docs/java-client/_index.md b/daprdocs/content/en/java-sdk-docs/java-client/_index.md deleted file mode 100644 index 5f33eb41e3..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-client/_index.md +++ /dev/null @@ -1,756 +0,0 @@ ---- -type: docs -title: "Getting started with the Dapr client Java SDK" -linkTitle: "Client" -weight: 3000 -description: How to get up and running with the Dapr Java SDK ---- - -The Dapr client package allows you to interact with other Dapr applications from a Java application. - -{{% alert title="Note" color="primary" %}} -If you haven't already, [try out one of the quickstarts]({{% ref quickstarts %}}) for a quick walk-through on how to use the Dapr Java SDK with an API building block. - -{{% /alert %}} - -## Prerequisites - -[Complete initial setup and import the Java SDK into your project]({{% ref java %}}) - -## Initializing the client -You can initialize a Dapr client as so: - -```java -DaprClient client = new DaprClientBuilder().build() -``` - -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 - -Initially, errors in Dapr followed the Standard gRPC error model. However, to provide more detailed and informative error -messages, in version 1.13 an enhanced error model has been introduced which aligns with the gRPC Richer error model. In -response, the Java SDK extended the DaprException to include the error details that were added in Dapr. - -Example of handling the DaprException and consuming the error details when using the Dapr Java SDK: - -```java -... - try { - client.publishEvent("unknown_pubsub", "mytopic", "mydata").block(); - } catch (DaprException exception) { - System.out.println("Dapr exception's error code: " + exception.getErrorCode()); - System.out.println("Dapr exception's message: " + exception.getMessage()); - // DaprException now contains `getStatusDetails()` to include more details about the error from Dapr runtime. - System.out.println("Dapr exception's reason: " + exception.getStatusDetails().get( - DaprErrorDetails.ErrorDetailType.ERROR_INFO, - "reason", - TypeRef.STRING)); - } -... -``` - -## 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 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 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 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 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 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 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 handleBulkMessage( - @RequestBody(required = false) BulkSubscribeMessage> bulkMessage) { - return Mono.fromCallable(() -> { - if (bulkMessage.getEntries().size() == 0) { - return new BulkSubscribeAppResponse(new ArrayList()); - } - - System.out.println("Bulk Subscriber received " + bulkMessage.getEntries().size() + " messages."); - - List entries = new ArrayList(); - 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 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 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 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 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 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 item = client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY).block(); - - // Get configurations for multiple keys - Mono> items = - client.getConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2); - - // Subscribe to configuration changes - Flux outFlux = client.subscribeConfiguration(CONFIG_STORE_NAME, CONFIG_KEY_1, CONFIG_KEY_2); - outFlux.subscribe(configItems -> configItems.forEach(...)); - - // Unsubscribe from configuration changes - Mono 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 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 result = previewClient.queryState(request, MyData.class).block(); - - // View Query state response - System.out.println("Found " + result.getResults().size() + " items."); - for (QueryStateItem 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. - -### Distributed lock - -```java -package io.dapr.examples.lock.grpc; - -import io.dapr.client.DaprClientBuilder; -import io.dapr.client.DaprPreviewClient; -import io.dapr.client.domain.LockRequest; -import io.dapr.client.domain.UnlockRequest; -import io.dapr.client.domain.UnlockResponseStatus; -import reactor.core.publisher.Mono; - -public class DistributedLockGrpcClient { - private static final String LOCK_STORE_NAME = "lockstore"; - - /** - * Executes various methods to check the different apis. - * - * @param args arguments - * @throws Exception throws Exception - */ - public static void main(String[] args) throws Exception { - try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) { - System.out.println("Using preview client..."); - tryLock(client); - unlock(client); - } - } - - /** - * Trying to get lock. - * - * @param client DaprPreviewClient object - */ - public static void tryLock(DaprPreviewClient client) { - System.out.println("*******trying to get a free distributed lock********"); - try { - LockRequest lockRequest = new LockRequest(LOCK_STORE_NAME, "resouce1", "owner1", 5); - Mono result = client.tryLock(lockRequest); - System.out.println("Lock result -> " + (Boolean.TRUE.equals(result.block()) ? "SUCCESS" : "FAIL")); - } catch (Exception ex) { - System.out.println(ex.getMessage()); - } - } - - /** - * Unlock a lock. - * - * @param client DaprPreviewClient object - */ - public static void unlock(DaprPreviewClient client) { - System.out.println("*******unlock a distributed lock********"); - try { - UnlockRequest unlockRequest = new UnlockRequest(LOCK_STORE_NAME, "resouce1", "owner1"); - Mono result = client.unlock(unlockRequest); - System.out.println("Unlock result ->" + result.block().name()); - } catch (Exception ex) { - System.out.println(ex.getMessage()); - } - } -} -``` - -- For a full how-to on distributed lock, visit [How-To: Use a Lock]({{% ref howto-use-distributed-lock.md %}}) -- Visit [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/lock) for complete code sample. - -### Workflow - -```java -package io.dapr.examples.workflows; - -import io.dapr.workflows.client.DaprWorkflowClient; -import io.dapr.workflows.client.WorkflowState; - -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("**GetWorkflowMetadata:Running Workflow**"); - WorkflowState workflowMetadata = client.getWorkflowState(instanceId, true); - System.out.printf("Result: %s%n", workflowMetadata); - - System.out.println(separatorStr); - System.out.println("**WaitForWorkflowStart**"); - try { - WorkflowState waitForWorkflowStartResult = - client.waitForWorkflowStart(instanceId, Duration.ofSeconds(60), true); - System.out.printf("Result: %s%n", waitForWorkflowStartResult); - } catch (TimeoutException ex) { - System.out.printf("waitForWorkflowStart 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("**waitForWorkflowCompletion**"); - try { - WorkflowState waitForWorkflowCompletionResult = - client.waitForWorkflowCompletion(instanceId, Duration.ofSeconds(60), true); - System.out.printf("Result: %s%n", waitForWorkflowCompletionResult); - } catch (TimeoutException ex) { - System.out.printf("waitForWorkflowCompletion has an exception:%s%n", ex); - } - - System.out.println(separatorStr); - System.out.println("**purgeWorkflow**"); - boolean purgeResult = client.purgeWorkflow(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 %}}). - -## Sidecar APIs - -#### Wait for sidecar -The `DaprClient` also provides a helper method to wait for the sidecar to become healthy (components only). When using -this method, be sure to specify a timeout in milliseconds and block() to wait for the result of a reactive operation. - -```java -// Wait for the Dapr sidecar to report healthy before attempting to use Dapr components. -try (DaprClient client = new DaprClientBuilder().build()) { - System.out.println("Waiting for Dapr sidecar ..."); - client.waitForSidecar(10000).block(); // Specify the timeout in milliseconds - System.out.println("Dapr sidecar is ready."); - ... -} - -// Perform Dapr component operations here i.e. fetching secrets or saving state. -``` - -### Shutdown the sidecar -```java -try (DaprClient client = new DaprClientBuilder().build()) { - logger.info("Sending shutdown request."); - client.shutdown().block(); - logger.info("Ensuring dapr has stopped."); - ... -} -``` - -Learn more about the [Dapr Java SDK packages available to add to your Java applications](https://dapr.github.io/java-sdk/). - -## Security - -### App API Token Authentication - -The building blocks like pubsub, input bindings, or jobs require Dapr to make incoming calls to your application, you can secure these requests using [Dapr App API Token Authentication]({{% ref app-api-token.md %}}). This ensures that only Dapr can invoke your application's endpoints. - -#### Understanding the two tokens - -Dapr uses two different tokens for securing communication. See [Properties]({{% ref properties.md %}}) for detailed information about both tokens: - -- **`DAPR_API_TOKEN`** (Your app → Dapr sidecar): Automatically handled by the Java SDK when using `DaprClient` -- **`APP_API_TOKEN`** (Dapr → Your app): Requires server-side validation in your application - -The examples below show how to implement server-side validation for `APP_API_TOKEN`. - -#### Implementing server-side token validation - -When using gRPC protocol, implement a server interceptor to capture the metadata. - -```java -import io.grpc.Context; -import io.grpc.Contexts; -import io.grpc.Metadata; -import io.grpc.ServerCall; -import io.grpc.ServerCallHandler; -import io.grpc.ServerInterceptor; - -public class SubscriberGrpcService extends AppCallbackGrpc.AppCallbackImplBase { - public static final Context.Key METADATA_KEY = Context.key("grpc-metadata"); - - // gRPC interceptor to capture metadata - public static class MetadataInterceptor implements ServerInterceptor { - @Override - public ServerCall.Listener interceptCall( - ServerCall call, - Metadata headers, - ServerCallHandler next) { - Context contextWithMetadata = Context.current().withValue(METADATA_KEY, headers); - return Contexts.interceptCall(contextWithMetadata, call, headers, next); - } - } - - // Your service methods go here... -} -``` - -Register the interceptor when building your gRPC server: - -```java -Server server = ServerBuilder.forPort(port) - .intercept(new SubscriberGrpcService.MetadataInterceptor()) - .addService(new SubscriberGrpcService()) - .build(); -server.start(); -``` - -Then, in your service methods, extract the token from metadata: - -```java -@Override -public void onTopicEvent(DaprAppCallbackProtos.TopicEventRequest request, - StreamObserver responseObserver) { - try { - // Extract metadata from context - Context context = Context.current(); - Metadata metadata = METADATA_KEY.get(context); - - if (metadata != null) { - String apiToken = metadata.get( - Metadata.Key.of("dapr-api-token", Metadata.ASCII_STRING_MARSHALLER)); - - // Validate token accordingly - } - - // Process the request - // ... - - } catch (Throwable e) { - responseObserver.onError(e); - } -} -``` - -#### Using with HTTP endpoints - -For HTTP-based endpoints, extract the token from the headers: - -```java -@RestController -public class SubscriberController { - - @PostMapping(path = "/endpoint") - public Mono handleRequest( - @RequestBody(required = false) byte[] body, - @RequestHeader Map headers) { - return Mono.fromRunnable(() -> { - try { - // Extract the token from headers - String apiToken = headers.get("dapr-api-token"); - - // Validate token accordingly - - // Process the request - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } -} -``` - -#### Examples - -For working examples with pubsub, bindings, and jobs: -- [PubSub with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/pubsub#app-api-token-authentication-optional) -- [Bindings with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http#app-api-token-authentication-optional) -- [Jobs with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/jobs#app-api-token-authentication-optional) - -## 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 %}}). diff --git a/daprdocs/content/en/java-sdk-docs/java-client/properties.md b/daprdocs/content/en/java-sdk-docs/java-client/properties.md deleted file mode 100644 index 87eb7a99c4..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-client/properties.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -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 Tokens - -Dapr supports two types of API tokens for securing communication: - -| Environment Variable | Description | Default | -|---------------------|-------------|---------| -| `DAPR_API_TOKEN` | API token for authenticating requests **from your app to the Dapr sidecar**. The Java SDK automatically includes this token in requests when using `DaprClient`. | `null` | -| `APP_API_TOKEN` | API token for authenticating requests **from Dapr to your app**. When set, Dapr includes this token in the `dapr-api-token` header/metadata when calling your application (for pubsub subscribers, input bindings, or job triggers). Your application must validate this token. | `null` | - -For implementation examples, see [App API Token Authentication]({{% ref java-client#app-api-token-authentication %}}). For more details, see [Dapr API token authentication](https://docs.dapr.io/operations/security/api-token/). - -### 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` | - -#### Inbound Message Settings -Configure gRPC inbound message settings using these environment variables: - -| Environment Variable | Description | Default | -|---------------------|-------------|---------| -| `DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES` | Dapr's maximum inbound message size for gRPC in bytes. This value sets the maximum size of a gRPC message that can be received by the application | `4194304` | -| `DAPR_GRPC_MAX_INBOUND_METADATA_SIZE_BYTES` | Dapr's maximum inbound metadata size for gRPC in bytes | `8192` | - -### 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 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. \ No newline at end of file diff --git a/daprdocs/content/en/java-sdk-docs/java-jobs/_index.md b/daprdocs/content/en/java-sdk-docs/java-jobs/_index.md deleted file mode 100644 index 9d017f7770..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-jobs/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: docs -title: "Jobs" -linkTitle: "Jobs" -weight: 3000 -description: With the Dapr Jobs package, you can interact with the Dapr Jobs APIs from a Java application to trigger future operations to run according to a predefined schedule with an optional payload. To get started, walk through the [Dapr Jobs]({{% ref java-jobs-howto.md %}}) how-to guide. ---- diff --git a/daprdocs/content/en/java-sdk-docs/java-jobs/java-jobs-howto.md b/daprdocs/content/en/java-sdk-docs/java-jobs/java-jobs-howto.md deleted file mode 100644 index e7c6346289..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-jobs/java-jobs-howto.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -type: docs -title: "How to: Author and manage Dapr Jobs in the Java SDK" -linkTitle: "How to: Author and manage Jobs" -weight: 20000 -description: How to get up and running with Jobs using the Dapr Java SDK ---- - -As part of this demonstration we will schedule a Dapr Job. The scheduled job will trigger an endpoint registered in the -same app. With the [provided jobs example](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/jobs), you will: - -- Schedule a Job [Job scheduling example](https://github.com/dapr/java-sdk/blob/master/examples/src/main/java/io/dapr/examples/jobs/DemoJobsClient.java) -- Register an endpoint for the dapr sidecar to invoke at trigger time [Endpoint Registration](https://github.com/dapr/java-sdk/blob/master/examples/src/main/java/io/dapr/examples/jobs/DemoJobsSpringApplication.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. -- [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 jobs 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 jobsapp --dapr-grpc-port 51439 --dapr-http-port 3500 --app-port 8080 -``` - -> Now, Dapr is listening for HTTP requests at `http://localhost:3500` and internal Jobs gRPC requests at `http://localhost:51439`. - -## Schedule and Get a job - -In the `DemoJobsClient` there are steps to schedule a job. Calling `scheduleJob` using the `DaprPreviewClient` -will schedule a job with the Dapr Runtime. - -```java -public class DemoJobsClient { - - /** - * The main method of this app to schedule and get jobs. - */ - public static void main(String[] args) throws Exception { - try (DaprPreviewClient client = new DaprClientBuilder().withPropertyOverrides(overrides).buildPreviewClient()) { - - // Schedule a job. - System.out.println("**** Scheduling a Job with name dapr-jobs-1 *****"); - ScheduleJobRequest scheduleJobRequest = new ScheduleJobRequest("dapr-job-1", - JobSchedule.fromString("* * * * * *")).setData("Hello World!".getBytes()); - client.scheduleJob(scheduleJobRequest).block(); - - System.out.println("**** Scheduling job dapr-jobs-1 completed *****"); - } - } -} -``` - -Call `getJob` to retrieve the job details that were previously created and scheduled. -``` -client.getJob(new GetJobRequest("dapr-job-1")).block() -``` - -Run the `DemoJobsClient` with the following command. - -```sh -java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.jobs.DemoJobsClient -``` - -### Sample output -``` -**** Scheduling a Job with name dapr-jobs-1 ***** -**** Scheduling job dapr-jobs-1 completed ***** -**** Retrieving a Job with name dapr-jobs-1 ***** -``` - -## Set up an endpoint to be invoked when the job is triggered - -The `DemoJobsSpringApplication` class starts a Spring Boot application that registers the endpoints specified in the `JobsController` -This endpoint acts like a callback for the scheduled job requests. - -```java -@RestController -public class JobsController { - - /** - * Handles jobs callback from Dapr. - * - * @param jobName name of the job. - * @param payload data from the job if payload exists. - * @return Empty Mono. - */ - @PostMapping("/job/{jobName}") - public Mono handleJob(@PathVariable("jobName") String jobName, - @RequestBody(required = false) byte[] payload) { - System.out.println("Job Name: " + jobName); - System.out.println("Job Payload: " + new String(payload)); - - return Mono.empty(); - } -} -``` - -Parameters: - -* `jobName`: The name of the triggered job. -* `payload`: Optional payload data associated with the job (as a byte array). - -Run the Spring Boot application with the following command. - -```sh -java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.jobs.DemoJobsSpringApplication -``` - -### Sample output -``` -Job Name: dapr-job-1 -Job Payload: Hello World! -``` - -## Delete a scheduled job - -```java -public class DemoJobsClient { - - /** - * The main method of this app deletes a job that was previously scheduled. - */ - public static void main(String[] args) throws Exception { - try (DaprPreviewClient client = new DaprClientBuilder().buildPreviewClient()) { - - // Delete a job. - System.out.println("**** Delete a Job with name dapr-jobs-1 *****"); - client.deleteJob(new DeleteJobRequest("dapr-job-1")).block(); - } - } -} -``` - -## Next steps -- [Learn more about Jobs]({{% ref jobs-overview.md %}}) -- [Jobs API reference]({{% ref jobs_api.md %}}) \ No newline at end of file diff --git a/daprdocs/content/en/java-sdk-docs/java-workflow/_index.md b/daprdocs/content/en/java-sdk-docs/java-workflow/_index.md deleted file mode 100644 index ecfb7adeb6..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-workflow/_index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -type: docs -title: "Workflow" -linkTitle: "Workflow" -weight: 3000 -description: How to get up and running with the Dapr Workflow extension ---- diff --git a/daprdocs/content/en/java-sdk-docs/java-workflow/java-workflow-howto.md b/daprdocs/content/en/java-sdk-docs/java-workflow/java-workflow-howto.md deleted file mode 100644 index 79c6e06d0c..0000000000 --- a/daprdocs/content/en/java-sdk-docs/java-workflow/java-workflow-howto.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -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 ---- - -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. - -- [Docker Desktop](https://www.docker.com/products/docker-desktop) - -- 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**"); - WorkflowState workflowMetadata = client.getWorkflowState(instanceId, true); - System.out.printf("Result: %s%n", workflowMetadata); - - System.out.println(separatorStr); - System.out.println("**WaitForWorkflowStart**"); - try { - WorkflowState waitForWorkflowStartResult = - client.waitForWorkflowStart(instanceId, Duration.ofSeconds(60), true); - System.out.printf("Result: %s%n", waitForWorkflowStartResult); - } catch (TimeoutException ex) { - System.out.printf("waitForWorkflowStart 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("**waitForWorkflowCompletion**"); - try { - WorkflowState waitForWorkflowCompletionResult = - client.waitForWorkflowCompletion(instanceId, Duration.ofSeconds(60), true); - System.out.printf("Result: %s%n", waitForWorkflowCompletionResult); - } catch (TimeoutException ex) { - System.out.printf("waitForWorkflowCompletion has an exception:%s%n", ex); - } - - System.out.println(separatorStr); - System.out.println("**purgeWorkflow**"); - boolean purgeResult = client.purgeWorkflow(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: ''] -******* -**WaitForWorkflowStart** -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 -******* -**WaitForWorkflowCompletion** -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: ''] -******* -**purgeWorkflow** -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-overview.md %}}) -- [Workflow API reference]({{% ref workflow_api.md %}}) - -## Advanced features - -### Task Execution Keys - -Task execution keys are unique identifiers generated by the durabletask-java library. They are stored in the `WorkflowActivityContext` and can be used to track and manage the execution of workflow activities. They are particularly useful for: - -1. **Idempotency**: Ensuring activities are not executed multiple times for the same task -2. **State Management**: Tracking the state of activity execution -3. **Error Handling**: Managing retries and failures in a controlled manner - -Here's an example of how to use task execution keys in your workflow activities: - -```java -public class TaskExecutionKeyActivity implements WorkflowActivity { - @Override - public Object run(WorkflowActivityContext ctx) { - // Get the task execution key for this activity - String taskExecutionKey = ctx.getTaskExecutionKey(); - - // Use the key to implement idempotency or state management - // For example, check if this task has already been executed - if (isTaskAlreadyExecuted(taskExecutionKey)) { - return getPreviousResult(taskExecutionKey); - } - - // Execute the activity logic - Object result = executeActivityLogic(); - - // Store the result with the task execution key - storeResult(taskExecutionKey, result); - - return result; - } -} -``` diff --git a/daprdocs/content/en/java-sdk-docs/spring-boot/_index.md b/daprdocs/content/en/java-sdk-docs/spring-boot/_index.md deleted file mode 100644 index fcfaacd1a6..0000000000 --- a/daprdocs/content/en/java-sdk-docs/spring-boot/_index.md +++ /dev/null @@ -1,347 +0,0 @@ ---- -type: docs -title: "Getting started with the Dapr and Spring Boot" -linkTitle: "Spring Boot Integration" -weight: 4000 -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 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 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, you can directly add the following dependencies to your project: - -``` - - io.dapr.spring - dapr-spring-boot-starter - 0.16.0 - - - io.dapr.spring - dapr-spring-boot-starter-test - 0.16.0 - test - -``` - -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 - -Once these dependencies are in your application, you can rely on Spring Boot autoconfiguration to autowire a `DaprClient` instance: - -```java -@Autowired -private DaprClient daprClient; - -``` - -This will connect to the default Dapr gRPC endpoint `localhost:50001`, requiring you to start Dapr outside of your application. - -{{% alert title="Note" color="primary" %}} -By default, the following properties are preconfigured for `DaprClient` and `DaprWorkflowClient`: -```properties -dapr.client.httpEndpoint=http://localhost -dapr.client.httpPort=3500 -dapr.client.grpcEndpoint=localhost -dapr.client.grpcPort=50001 -dapr.client.apiToken= -``` -These values are used by default, but you can override them in your `application.properties` file to suit your environment. Please note that both kebab case and camel case are supported. -{{% /alert %}} - -You can use the `DaprClient` to interact with the Dapr APIs anywhere in your application, for example from inside a REST endpoint: - -```java -@RestController -public class DemoRestController { - @Autowired - private DaprClient daprClient; - - @PostMapping("/store") - public void storeOrder(@RequestBody Order order){ - daprClient.saveState("kvstore", order.orderId(), order).block(); - } -} - -record Order(String orderId, Integer amount){} -``` - -If you want to avoid managing Dapr outside of your Spring Boot application, you can rely on [Testcontainers](https://testcontainers.com/) to bootstrap Dapr beside your application for development purposes. -To do this we can create a test configuration that uses `Testcontainers` to bootstrap all we need to develop our applications using the Dapr APIs. - -Using [Testcontainers](https://testcontainers.com/) and Dapr integrations, we let the `@TestConfiguration` bootstrap Dapr for our applications. -Notice that for this example, we are configuring Dapr with a Statestore component called `kvstore` that connects to an instance of `PostgreSQL` also bootstrapped by Testcontainers. - -```java -@TestConfiguration(proxyBeanMethods = false) -public class DaprTestContainersConfig { - @Bean - @ServiceConnection - public DaprContainer daprContainer(Network daprNetwork, PostgreSQLContainer postgreSQLContainer){ - - return new DaprContainer("daprio/daprd:1.16.0-rc.5") - .withAppName("producer-app") - .withNetwork(daprNetwork) - .withComponent(new Component("kvstore", "state.postgresql", "v1", STATE_STORE_PROPERTIES)) - .withComponent(new Component("kvbinding", "bindings.postgresql", "v1", BINDING_PROPERTIES)) - .dependsOn(postgreSQLContainer); - } -} -``` - -Inside the test classpath you can add a new Spring Boot Application that uses this configuration for tests: - -```java -@SpringBootApplication -public class TestProducerApplication { - - public static void main(String[] args) { - - SpringApplication - .from(ProducerApplication::main) - .with(DaprTestContainersConfig.class) - .run(args); - } - -} -``` - -Now you can start your application with: -```bash -mvn spring-boot:test-run -``` - -Running this command will start the application, using the provided test configuration that includes the Testcontainers and Dapr integration. In the logs you should be able to see that the `daprd` and the `placement` service containers were started for your application. - -Besides the previous configuration (`DaprTestContainersConfig`) your tests shouldn't be testing Dapr itself, just the REST endpoints that your application is exposing. - - -## Leveraging Spring & Spring Boot programming model with Dapr - -The Java SDK allows you to interface with all of the [Dapr building blocks]({{% ref building-blocks %}}). -But if you want to leverage the Spring and Spring Boot programming model you can use the `dapr-spring-boot-starter` integration. -This includes implementations of Spring Data (`KeyValueTemplate` and `CrudRepository`) as well as a `DaprMessagingTemplate` for producing and consuming messages -(similar to [Spring Kafka](https://spring.io/projects/spring-kafka), [Spring Pulsar](https://spring.io/projects/spring-pulsar) and [Spring AMQP for RabbitMQ](https://spring.io/projects/spring-amqp)) and Dapr workflows. - -## Using Spring Data `CrudRepository` and `KeyValueTemplate` - -You can use well known Spring Data constructs relying on a Dapr-based implementation. -With Dapr, you don't need to add any infrastructure-related driver or client, making your Spring application lighter and decoupled from the environment where it is running. - -Under the hood these implementations use the Dapr Statestore and Binding APIs. - -### Configuration parameters - -With Spring Data abstractions you can configure which statestore and bindings will be used by Dapr to connect to the available infrastructure. -This can be done by setting the following properties: - -```properties -dapr.statestore.name=kvstore -dapr.statestore.binding=kvbinding -``` - -Then you can `@Autowire` a `KeyValueTemplate` or a `CrudRepository` like this: - -```java -@RestController -@EnableDaprRepositories -public class OrdersRestController { - @Autowired - private OrderRepository repository; - - @PostMapping("/orders") - public void storeOrder(@RequestBody Order order){ - repository.save(order); - } - - @GetMapping("/orders") - public Iterable getAll(){ - return repository.findAll(); - } - - -} -``` - -Where `OrderRepository` is defined in an interface that extends the Spring Data `CrudRepository` interface: - -```java -public interface OrderRepository extends CrudRepository {} -``` - -Notice that the `@EnableDaprRepositories` annotation does all the magic of wiring the Dapr APIs under the `CrudRespository` interface. -Because Dapr allow users to interact with different StateStores from the same application, as a user you need to provide the following beans as a Spring Boot `@Configuration`: - -```java -@Configuration -@EnableConfigurationProperties({DaprStateStoreProperties.class}) -public class ProducerAppConfiguration { - - @Bean - public KeyValueAdapterResolver keyValueAdapterResolver(DaprClient daprClient, ObjectMapper mapper, DaprStateStoreProperties daprStatestoreProperties) { - String storeName = daprStatestoreProperties.getName(); - String bindingName = daprStatestoreProperties.getBinding(); - - return new DaprKeyValueAdapterResolver(daprClient, mapper, storeName, bindingName); - } - - @Bean - public DaprKeyValueTemplate daprKeyValueTemplate(KeyValueAdapterResolver keyValueAdapterResolver) { - return new DaprKeyValueTemplate(keyValueAdapterResolver); - } - -} -``` - -## Using Spring Messaging for producing and consuming events - -Similar to Spring Kafka, Spring Pulsar and Spring AMQP you can use the `DaprMessagingTemplate` to publish messages to the configured infrastructure. To consume messages you can use the `@Topic` annotation (soon to be renamed to `@DaprListener`). - -To publish events/messages you can `@Autowired` the `DaprMessagingTemplate` in your Spring application. -For this example we will be publishing `Order` events and we are sending messages to the topic named `topic`. - -```java -@Autowired -private DaprMessagingTemplate messagingTemplate; - -@PostMapping("/orders") -public void storeOrder(@RequestBody Order order){ - repository.save(order); - messagingTemplate.send("topic", order); -} - -``` - -Similarly to the `CrudRepository` we need to specify which PubSub broker do we want to use to publish and consume our messages. - -```properties -dapr.pubsub.name=pubsub -``` - -Because with Dapr you can connect to multiple PubSub brokers you need to provide the following bean to let Dapr know which PubSub broker your `DaprMessagingTemplate` will use: -```java -@Bean -public DaprMessagingTemplate messagingTemplate(DaprClient daprClient, - DaprPubSubProperties daprPubSubProperties) { - return new DaprMessagingTemplate<>(daprClient, daprPubSubProperties.getName()); -} -``` - -Finally, because Dapr PubSub requires a bidirectional connection between your application and Dapr you need to expand your Testcontainers configuration with a few parameters: - -```java -@Bean -@ServiceConnection -public DaprContainer daprContainer(Network daprNetwork, PostgreSQLContainer postgreSQLContainer, RabbitMQContainer rabbitMQContainer){ - - return new DaprContainer("daprio/daprd:1.16.0-rc.5") - .withAppName("producer-app") - .withNetwork(daprNetwork) - .withComponent(new Component("kvstore", "state.postgresql", "v1", STATE_STORE_PROPERTIES)) - .withComponent(new Component("kvbinding", "bindings.postgresql", "v1", BINDING_PROPERTIES)) - .withComponent(new Component("pubsub", "pubsub.rabbitmq", "v1", rabbitMqProperties)) - .withAppPort(8080) - .withAppChannelAddress("host.testcontainers.internal") - .dependsOn(rabbitMQContainer) - .dependsOn(postgreSQLContainer); -} -``` - -Now, in the Dapr configuration we have included a `pubsub` component that will connect to an instance of RabbitMQ started by Testcontainers. -We have also set two important parameters `.withAppPort(8080)` and `.withAppChannelAddress("host.testcontainers.internal")` which allows Dapr to -contact back to the application when a message is published in the broker. - -To listen to events/messages you need to expose an endpoint in the application that will be responsible to receive the messages. -If you expose a REST endpoint you can use the `@Topic` annotation to let Dapr know where it needs to forward the events/messages too: - -```java -@PostMapping("subscribe") -@Topic(pubsubName = "pubsub", name = "topic") -public void subscribe(@RequestBody CloudEvent cloudEvent){ - events.add(cloudEvent); -} -``` - -Upon bootstrapping your application, Dapr will register the subscription to messages to be forwarded to the `subscribe` endpoint exposed by your application. - -If you are writing tests for these subscribers you need to ensure that Testcontainers knows that your application will be running on port 8080, -so containers started with Testcontainers know where your application is: - -```java -@BeforeAll -public static void setup(){ - org.testcontainers.Testcontainers.exposeHostPorts(8080); -} -``` - -You can check and run the [full example source code here](https://github.com/salaboy/dapr-spring-boot-docs-examples). - -## Using Dapr Workflows with Spring Boot - -Following the same approach that we used for Spring Data and Spring Messaging, the `dapr-spring-boot-starter` brings Dapr Workflow integration for Spring Boot users. - -To work with Dapr Workflows you need to define and implement your workflows using code. The Dapr Spring Boot Starter makes your life easier by managing `Workflow`s and `WorkflowActivity`s as Spring beans. - -In order to enable the automatic bean discovery you can annotate your `@SpringBootApplication` with the `@EnableDaprWorkflows` annotation: - -``` -@SpringBootApplication -@EnableDaprWorkflows -public class MySpringBootApplication {} -``` - -By adding this annotation, all the `WorkflowActivity`s will be automatically managed by Spring and registered to the workflow engine. - -By having all `WorkflowActivity`s as managed beans we can use Spring `@Autowired` mechanism to inject any bean that our workflow activity might need to implement its functionality, for example the `@RestTemplate`: - -``` -public class MyWorkflowActivity implements WorkflowActivity { - - @Autowired - private RestTemplate restTemplate; -``` - -You can also `@Autowired` the `DaprWorkflowClient` to create new instances of your workflows. - -``` -@Autowired -private DaprWorkflowClient daprWorkflowClient; -``` - -This enable applications to schedule new workflow instances and raise events. - -``` -String instanceId = daprWorkflowClient.scheduleNewWorkflow(MyWorkflow.class, payload); -``` - -and - -``` -daprWorkflowClient.raiseEvent(instanceId, "MyEvenet", event); -``` - -Check the [Dapr Workflow documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) for more information about how to work with Dapr Workflows. - - -## Next steps - -Learn more about the [Dapr Java SDK packages available to add to your Java applications](https://dapr.github.io/java-sdk/). - -## Related links -- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)