Make DaprClient Closeable and properly close GRPC Managed Channel (#311)

This commit is contained in:
Mukundan Sundararajan 2020-07-30 13:34:04 -07:00 committed by GitHub
parent a388a6a90f
commit 14fded8d18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 511 additions and 342 deletions

View File

@ -8,6 +8,8 @@ package io.dapr.examples.bindings.http;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import java.io.IOException;
/**
* Service for output binding example.
* 1. From your repo root, build and install jars:
@ -36,35 +38,36 @@ public class OutputBindingExample {
* @param args Not used.
*/
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
public static void main(String[] args) {
DaprClient client = new DaprClientBuilder().build();
public static void main(String[] args) throws IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
int count = 0;
while (!Thread.currentThread().isInterrupted()) {
String message = "Message #" + (count++);
int count = 0;
while (!Thread.currentThread().isInterrupted()) {
String message = "Message #" + (count++);
// Randomly decides between a class type or string type to be sent.
if (Math.random() >= 0.5) {
// This is an example of sending data in a user-defined object. The input binding will receive:
// {"message":"hello"}
MyClass myClass = new MyClass();
myClass.message = message;
// Randomly decides between a class type or string type to be sent.
if (Math.random() >= 0.5) {
// This is an example of sending data in a user-defined object. The input binding will receive:
// {"message":"hello"}
MyClass myClass = new MyClass();
myClass.message = message;
System.out.println("sending a class with message: " + myClass.message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass).block();
} else {
System.out.println("sending a plain string: " + message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message).block();
System.out.println("sending a class with message: " + myClass.message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass).block();
} else {
System.out.println("sending a plain string: " + message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message).block();
}
try {
Thread.sleep((long) (10000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
try {
Thread.sleep((long) (10000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
System.out.println("Done.");
}
System.out.println("Done.");
}
}

View File

@ -101,25 +101,45 @@ dapr run --components-path ./components --app-id inputbinding --app-port 3000 --
The output binding application is a simple java class with a main method that uses the Dapr Client to invoke binding.
In the `OutputBindingExample.java` file, you will find the `OutputBindingExample` class, containing the main method. The main method declares a Dapr Client using the `DaprClientBuilder` class. Notice that this builder gets two serializer implementations in the constructor: One is for Dapr's sent and recieved objects, and second is for objects to be persisted. The client publishes events using `invokeBinding` method. See the code snippet below:
In the `OutputBindingExample.java` file, you will find the `OutputBindingExample` class, containing the main method. The main method declares a Dapr Client using the `DaprClientBuilder` class. Notice that this builder gets two serializer implementations in the constructor: One is for Dapr's sent and recieved objects, and second is for objects to be persisted. The client publishes events using `invokeBinding` method. The Dapr client is also within a try-with-resource block to properly close the client at the end. See the code snippet below:
```java
public class OutputBindingExample {
public class OutputBindingExample{
///...
static final String BINDING_NAME = "sample123";
static final String BINDING_OPERATION = "create";
///...
public static void main(String[] args) {
DaprClient client = new DaprClientBuilder().build();
///...
MyClass myClass = new MyClass();
myClass.message = message;
System.out.println("sending an object instance with message: " + myClass.message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass); //Binding a data object
///...
System.out.println("sending a plain string: " + m);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message); //Binding a plain string text
public static void main(String[] args) throws IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
int count = 0;
while (!Thread.currentThread().isInterrupted()) {
String message = "Message #" + (count++);
// Randomly decides between a class type or string type to be sent.
if (Math.random() >= 0.5) {
// This is an example of sending data in a user-defined object. The input binding will receive:
// {"message":"hello"}
MyClass myClass = new MyClass();
myClass.message = message;
System.out.println("sending a class with message: " + myClass.message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, myClass).block();
} else {
System.out.println("sending a plain string: " + message);
client.invokeBinding(BINDING_NAME, BINDING_OPERATION, message).block();
}
try {
Thread.sleep((long) (10000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
System.out.println("Done.");
}
}
///...
}
@ -145,4 +165,9 @@ Once running, the InputBindingExample should print the output as follows:
Events have been retrieved from the binding.
For bringing down the kafka cluster that was started in the beginning, run
```sh
docker-compose -f ./src/main/java/io/dapr/examples/bindings/http/docker-compose-single-kafka.yml down
```
For more details on Dapr Spring Boot integration, please refer to [Dapr Spring Boot](../../../springboot/DaprApplication.java) Application implementation.

View File

@ -9,6 +9,8 @@ import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.HttpExtension;
import java.io.IOException;
/**
* 1. Build and install jars:
* mvn clean install
@ -23,24 +25,25 @@ public class HelloWorldClient {
*
* @param args Array of messages to be sent.
*/
public static void main(String[] args) throws InterruptedException {
DaprClient client = new DaprClientBuilder().build();
public static void main(String[] args) throws InterruptedException, IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
String serviceAppId = "hellogrpc";
String method = "say";
String serviceAppId = "hellogrpc";
String method = "say";
int count = 0;
while (true) {
String message = "Message #" + (count++);
System.out.println("Sending message: " + message);
client.invokeService(serviceAppId, method, message, HttpExtension.NONE).block();
System.out.println("Message sent: " + message);
int count = 0;
while (true) {
String message = "Message #" + (count++);
System.out.println("Sending message: " + message);
client.invokeService(serviceAppId, method, message, HttpExtension.NONE).block();
System.out.println("Message sent: " + message);
Thread.sleep(1000);
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
Thread.sleep(1000);
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
}
}

View File

@ -87,26 +87,31 @@ The other component is the client. It will send one message per second to the se
private static class HelloWorldClient {
///...
public static void main(String[] args) {
DaprClient client = new DaprClientBuilder().build();
try (DaprClient client = new DaprClientBuilder().build()) {
String serviceAppId = "hellogrpc";
String method = "say";
String serviceAppId = "hellogrpc";
String method = "say";
int count = 0;
while (true) {
String message = "Message #" + (count++);
System.out.println("Sending message: " + message);
client.invokeService(serviceAppId, method, message, HttpExtension.NONE).block();
System.out.println("Message sent: " + message);
int count = 0;
while (true) {
String message = "Message #" + (count++);
System.out.println("Sending message: " + message);
client.invokeService(serviceAppId, method, message, HttpExtension.NONE).block();
System.out.println("Message sent: " + message);
Thread.sleep(1000);
Thread.sleep(1000);
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
}
///...
}
```
First, it creates an instance of `DaprClient` via `DaprClientBuilder`. The protocol used by DaprClient is transparent to the application. The HTTP and GRPC ports used by Dapr's sidecar are automatically chosen and exported as environment variables: `DAPR_HTTP_PORT` and `DAPR_GRPC_PORT`. Dapr's Java SDK references these environment variables when communicating to Dapr's sidecar.
First, it creates an instance of `DaprClient` via `DaprClientBuilder`. The protocol used by DaprClient is transparent to the application. The HTTP and GRPC ports used by Dapr's sidecar are automatically chosen and exported as environment variables: `DAPR_HTTP_PORT` and `DAPR_GRPC_PORT`. Dapr's Java SDK references these environment variables when communicating to Dapr's sidecar. The Dapr client is also within a try-with-resource block to properly close the client at the end.
Finally, it will go through in an infinite loop and invoke the `say` method every second. Notice the use of `block()` on the return from `invokeService` - it is required to actually make the service invocation via a [Mono](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html) object.

View File

@ -9,13 +9,15 @@ import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.client.HttpExtension;
import java.io.IOException;
/**
* 1. Build and install jars:
* mvn clean install
* 2. Send messages to the server:
* dapr run --components-path ./examples/components \
* --port 3006 -- java -jar examples/target/dapr-java-sdk-examples-exec.jar \
* io.dapr.examples.invoke.http.InvokeClient 'message one' 'message two'
* --port 3006 -- java -jar examples/target/dapr-java-sdk-examples-exec.jar \
* io.dapr.examples.invoke.http.InvokeClient 'message one' 'message two'
*/
public class InvokeClient {
@ -29,16 +31,17 @@ public class InvokeClient {
*
* @param args Messages to be sent as request for the invoke API.
*/
public static void main(String[] args) {
DaprClient client = (new DaprClientBuilder()).build();
for (String message : args) {
byte[] response = client.invokeService(SERVICE_APP_ID, "say", message, HttpExtension.POST, null,
byte[].class).block();
System.out.println(new String(response));
}
public static void main(String[] args) throws IOException {
try (DaprClient client = (new DaprClientBuilder()).build()) {
for (String message : args) {
byte[] response = client.invokeService(SERVICE_APP_ID, "say", message, HttpExtension.POST, null,
byte[].class).block();
System.out.println(new String(response));
}
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
}

View File

@ -110,12 +110,17 @@ public class InvokeClient {
private static final String SERVICE_APP_ID = "invokedemo";
///...
public static void main(String[] args) {
DaprClient client = (new DaprClientBuilder()).build();
for (String message : args) {
byte[] response = client.invokeService(SERVICE_APP_ID, "say",
message, HttpExtension.POST, null, byte[].class).block();
System.out.println(new String(response));
public static void main(String[] args) throws IOException {
try (DaprClient client = (new DaprClientBuilder()).build()) {
for (String message : args) {
byte[] response = client.invokeService(SERVICE_APP_ID, "say", message, HttpExtension.POST, null,
byte[].class).block();
System.out.println(new String(response));
}
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
///...
@ -124,7 +129,7 @@ private static final String SERVICE_APP_ID = "invokedemo";
The class knows the app id for the remote application. It uses the the static `Dapr.getInstance().invokeService` method to invoke the remote method defining the parameters: The verb, application id, method name, and proper data and metadata, as well as the type of the expected return type. The returned payload for this method invocation is plain text and not a [JSON String](https://www.w3schools.com/js/js_json_datatypes.asp), so we expect `byte[]` to get the raw response and not try to deserialize it.
Execute the follow script in order to run the InvokeClient example, passing two messages for the remote method:
Execute the follow script in order to run the InvokeClient example, passing two messages for the remote method:
```sh
dapr run --components-path ./components --port 3006 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.invoke.http.InvokeClient 'message one' 'message two'
```

View File

@ -8,6 +8,7 @@ package io.dapr.examples.pubsub.http;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import java.io.IOException;
import java.util.Collections;
/**
@ -32,31 +33,32 @@ public class Publisher {
*/
public static void main(String[] args) throws Exception {
//Creating the DaprClient: Using the default builder client produces an HTTP Dapr Client
DaprClient client = new DaprClientBuilder().build();
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
//Publishing messages
client.publishEvent(TOPIC_NAME, message).block();
System.out.println("Published message: " + message);
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
//Publishing messages
client.publishEvent(TOPIC_NAME, message).block();
System.out.println("Published message: " + message);
try {
Thread.sleep((long)(1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
return;
try {
Thread.sleep((long) (1000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
return;
}
}
//Publishing a single bite: Example of non-string based content published
client.publishEvent(
TOPIC_NAME,
new byte[]{1},
Collections.singletonMap("content-type", "application/octet-stream")).block();
System.out.println("Published one byte.");
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done.");
}
//Publishing a single bite: Example of non-string based content published
client.publishEvent(
TOPIC_NAME,
new byte[] { 1 },
Collections.singletonMap("content-type", "application/octet-stream")).block();
System.out.println("Published one byte.");
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done.");
}
}

View File

@ -84,20 +84,23 @@ dapr run --components-path ./components --app-id subscriber --app-port 3000 --po
The other component is the publisher. It is a simple java application with a main method that uses the Dapr HTTP Client to publish 10 messages to an specific topic.
In the `Publisher.java` file, you will find the `Publisher` class, containing the main method. The main method declares a Dapr Client using the `DaprClientBuilder` class. Notice that this builder gets two serializer implementations in the constructor: One is for Dapr's sent and recieved objects, and second is for objects to be persisted. The client publishes messages using `publishEvent` method. See the code snippet below:
In the `Publisher.java` file, you will find the `Publisher` class, containing the main method. The main method declares a Dapr Client using the `DaprClientBuilder` class. Notice that this builder gets two serializer implementations in the constructor: One is for Dapr's sent and recieved objects, and second is for objects to be persisted. The client publishes messages using `publishEvent` method. The Dapr client is also within a try-with-resource block to properly close the client at the end. See the code snippet below:
```java
public class Publisher {
private static final int NUM_MESSAGES = 10;
private static final String TOPIC_NAME = "testingtopic";
///...
public static void main(String[] args) throws Exception {
DaprClient client = new DaprClientBuilder().build();
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
client.publishEvent(TOPIC_NAME, message).block();
System.out.println("Published message: " + message);
//..
}
//Creating the DaprClient: Using the default builder client produces an HTTP Dapr Client
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
//Publishing messages
client.publishEvent(TOPIC_NAME, message).block();
System.out.println("Published message: " + message);
//...
}
}
}
///...
}

View File

@ -43,8 +43,8 @@ mvn install
Before getting into the application code, follow these steps in order to setup a local instance of Vault. This is needed for the local instances. Steps are:
1. navigate to the [repo-root] with `cd java-sdk`
2. Run `docker-compose -f ./examples/src/main/java/io/dapr/examples/secrets/docker-compose-vault.yml up -d` to run the container locally
1. navigate to the [examples] with `cd examples`
2. Run `docker-compose -f ./src/main/java/io/dapr/examples/secrets/docker-compose-vault.yml up -d` to run the container locally
3. Run `docker ps` to see the container running locally:
```bash
@ -61,6 +61,8 @@ Dapr's API for secret store only support read operations. For this sample to run
vault login myroot
```
> Note: If you get `http: server gave HTTP response to HTTPS client` make sure the local vault address is set `export VAULT_ADDR=http://127.0.0.1:8200/`
2. Create secret (replace `[my favorite movie]` with a title of our choice):
```bash
vault kv put secret/dapr/movie title="[my favorite movie]"
@ -78,24 +80,36 @@ The example's main function is in `SecretClient.java`.
```java
public class SecretClient {
private static final String SECRET_STORE_NAME = "vault";
/**
* Identifier in Dapr for the secret store.
*/
private static final String SECRET_STORE_NAME = "vault";
/**
* JSON Serializer to print output.
*/
private static final ObjectMapper JSON_SERIALIZER = new ObjectMapper();
///...
public static void main(String[] args) throws Exception {
///...
String secretKey = args[0];
DaprClient client = (new DaprClientBuilder()).build();
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
}
if (args.length != 1) {
throw new IllegalArgumentException("Use one argument: secret's key to be retrieved.");
}
String secretKey = args[0];
try (DaprClient client = (new DaprClientBuilder()).build()) {
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
}
}
///...
}
```
The program receives one and only one argument: the secret's key to be fetched.
After identifying the key to be fetched, it will retrieve it from the pre-defined secret store: `vault`.
The secret store's name **must** match the component's name defined in `< repo dir >/examples/components/hashicorp_vault.yaml`.
The Dapr client is also within a try-with-resource block to properly close the client at the end.
Execute the follow script in order to run the example:
```sh
@ -109,4 +123,11 @@ Once running, the program should print the output as follows:
== APP == {"title":"[my favorite movie]"}
```
To close the app, press CTRL+c.
To cleanup and bring the vault container down, run
```sh
docker-compose -f ./src/main/java/io/dapr/examples/secrets/docker-compose-vault.yml down
```
Thanks for playing.

View File

@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import java.io.IOException;
import java.util.Map;
/**
@ -44,8 +45,9 @@ public class SecretClient {
}
String secretKey = args[0];
DaprClient client = (new DaprClientBuilder()).build();
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
try (DaprClient client = (new DaprClientBuilder()).build()) {
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, secretKey).block();
System.out.println(JSON_SERIALIZER.writeValueAsString(secret));
}
}
}

View File

@ -32,36 +32,41 @@ cd examples
### Running the StateClient
This example uses the Java SDK Dapr client in order to save, retrieve and delete a state, in this case, an instance of a class. Multiple state stores are supported since Dapr 0.4. See the code snippet bellow:
```
```java
public class StateClient {
///...
private static final String STATE_STORE_NAME = "statestore";
private static final String KEY_NAME = "mykey";
///...
public static void main(String[] args) {
DaprClient client = new DaprClientBuilder().build();
String message = args.length == 0 ? " " : args[0];
MyClass myClass = new MyClass();
myClass.message = message;
client.saveState(KEY_NAME, myClass).block();
System.out.println("Saving class with message: " + message);
Mono<State<MyClass>> retrievedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Retrieved class message from state: " + (retrievedMessageMono.block().getValue()).message);
System.out.println("Deleting state...");
Mono<Void> mono = client.deleteState(STATE_STORE_NAME, KEY_NAME);
mono.block();
Mono<State<MyClass>> retrievedDeletedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Trying to retrieve deleted state: " + retrievedDeletedMessageMono.block().getValue());
}
public static void main(String[] args) throws IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
String message = args.length == 0 ? " " : args[0];
MyClass myClass = new MyClass();
myClass.message = message;
client.saveState(STATE_STORE_NAME, KEY_NAME, myClass).block();
System.out.println("Saving class with message: " + message);
Mono<State<MyClass>> retrievedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Retrieved class message from state: " + (retrievedMessageMono.block().getValue()).message);
System.out.println("Deleting state...");
Mono<Void> mono = client.deleteState(STATE_STORE_NAME, KEY_NAME);
mono.block();
Mono<State<MyClass>> retrievedDeletedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Trying to retrieve deleted state: " + retrievedDeletedMessageMono.block().getValue());
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
}
```
The code uses the `DaprClient` created by the `DaprClientBuilder`. Notice that this builder uses default settings. Internally, it is using `DefaultObjectSerializer` for two properties: `objectSerializer` is for Dapr's sent and recieved objects, and `stateSerializer` is for objects to be persisted. This client performs three operations: `client.saveState(...)` for persisting an instance of `MyClass`, then uses the `client.getState(...)` operation in order to retrieve back the persisted state using the same key. `client.deleteState(...)` operation is used to remove the persisted state. Finally, the code tries to retrieve the deleted state, which should not be found.
The code uses the `DaprClient` created by the `DaprClientBuilder`. Notice that this builder uses default settings. Internally, it is using `DefaultObjectSerializer` for two properties: `objectSerializer` is for Dapr's sent and received objects, and `stateSerializer` is for objects to be persisted. This client performs three operations: `client.saveState(...)` for persisting an instance of `MyClass`, then uses the `client.getState(...)` operation in order to retrieve back the persisted state using the same key. `client.deleteState(...)` operation is used to remove the persisted state. Finally, the code tries to retrieve the deleted state, which should not be found. The Dapr client is also within a try-with-resource block to properly close the client at the end.
### Running the example

View File

@ -10,6 +10,8 @@ import io.dapr.client.DaprClientBuilder;
import io.dapr.client.domain.State;
import reactor.core.publisher.Mono;
import java.io.IOException;
/**
* 1. Build and install jars:
* mvn clean install
@ -32,28 +34,29 @@ public class StateClient {
* Executes the sate actions.
* @param args messages to be sent as state value.
*/
public static void main(String[] args) {
DaprClient client = new DaprClientBuilder().build();
String message = args.length == 0 ? " " : args[0];
public static void main(String[] args) throws IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
String message = args.length == 0 ? " " : args[0];
MyClass myClass = new MyClass();
myClass.message = message;
MyClass myClass = new MyClass();
myClass.message = message;
client.saveState(STATE_STORE_NAME, KEY_NAME, myClass).block();
System.out.println("Saving class with message: " + message);
client.saveState(STATE_STORE_NAME, KEY_NAME, myClass).block();
System.out.println("Saving class with message: " + message);
Mono<State<MyClass>> retrievedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Retrieved class message from state: " + (retrievedMessageMono.block().getValue()).message);
Mono<State<MyClass>> retrievedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Retrieved class message from state: " + (retrievedMessageMono.block().getValue()).message);
System.out.println("Deleting state...");
Mono<Void> mono = client.deleteState(STATE_STORE_NAME, KEY_NAME);
mono.block();
System.out.println("Deleting state...");
Mono<Void> mono = client.deleteState(STATE_STORE_NAME, KEY_NAME);
mono.block();
Mono<State<MyClass>> retrievedDeletedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Trying to retrieve deleted state: " + retrievedDeletedMessageMono.block().getValue());
Mono<State<MyClass>> retrievedDeletedMessageMono = client.getState(STATE_STORE_NAME, KEY_NAME, MyClass.class);
System.out.println("Trying to retrieve deleted state: " + retrievedDeletedMessageMono.block().getValue());
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
// This is an example, so for simplicity we are just exiting here.
// Normally a dapr app would be a web service and not exit main.
System.out.println("Done");
}
}
}

View File

@ -73,51 +73,52 @@ public class BindingIT extends BaseIT {
daprRun.switchToHTTP();
}
DaprClient client = new DaprClientBuilder().build();
try(DaprClient client = new DaprClientBuilder().build()) {
// This is an example of sending data in a user-defined object. The input binding will receive:
// {"message":"hello"}
MyClass myClass = new MyClass();
myClass.message = "hello";
// This is an example of sending data in a user-defined object. The input binding will receive:
// {"message":"hello"}
MyClass myClass = new MyClass();
myClass.message = "hello";
System.out.println("sending first message");
client.invokeBinding(
BINDING_NAME, BINDING_OPERATION, myClass, Collections.singletonMap("MyMetadata", "MyValue"), Void.class).block();
System.out.println("sending first message");
client.invokeBinding(
BINDING_NAME, BINDING_OPERATION, myClass, Collections.singletonMap("MyMetadata", "MyValue"), Void.class).block();
// This is an example of sending a plain string. The input binding will receive
// cat
final String m = "cat";
System.out.println("sending " + m);
client.invokeBinding(
BINDING_NAME, BINDING_OPERATION, m, Collections.singletonMap("MyMetadata", "MyValue"), Void.class).block();
// This is an example of sending a plain string. The input binding will receive
// cat
final String m = "cat";
System.out.println("sending " + m);
client.invokeBinding(
BINDING_NAME, BINDING_OPERATION, m, Collections.singletonMap("MyMetadata", "MyValue"), Void.class).block();
// Metadata is not used by Kafka component, so it is not possible to validate.
callWithRetry(() -> {
System.out.println("Checking results ...");
final List<String> messages =
client.invokeService(
daprRun.getAppName(),
"messages",
null,
HttpExtension.GET,
List.class).block();
assertEquals(2, messages.size());
// Metadata is not used by Kafka component, so it is not possible to validate.
callWithRetry(() -> {
System.out.println("Checking results ...");
final List<String> messages =
client.invokeService(
daprRun.getAppName(),
"messages",
null,
HttpExtension.GET,
List.class).block();
assertEquals(2, messages.size());
MyClass resultClass = null;
try {
resultClass = new ObjectMapper().readValue(messages.get(0), MyClass.class);
} catch (Exception ex) {
ex.printStackTrace();
fail("Error on decode message 1");
}
MyClass resultClass = null;
try {
resultClass = new ObjectMapper().readValue(messages.get(0), MyClass.class);
} catch (Exception ex) {
ex.printStackTrace();
fail("Error on decode message 1");
}
try {
assertEquals("cat", new ObjectMapper().readValue(messages.get(1), String.class));
} catch (Exception ex) {
ex.printStackTrace();
fail("Error on decode message 2");
}
assertEquals("hello", resultClass.message);
}, 8000);
try {
assertEquals("cat", new ObjectMapper().readValue(messages.get(1), String.class));
} catch (Exception ex) {
ex.printStackTrace();
fail("Error on decode message 2");
}
assertEquals("hello", resultClass.message);
}, 8000);
}
}
}

View File

@ -6,11 +6,13 @@ import io.dapr.client.DaprHttp;
import io.dapr.client.HttpExtension;
import io.dapr.it.BaseIT;
import io.dapr.it.DaprRun;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.*;
import static org.junit.Assert.assertEquals;
@ -58,65 +60,65 @@ public class MethodInvokeIT extends BaseIT {
}
@Test
public void testInvoke() {
public void testInvoke() throws IOException {
// At this point, it is guaranteed that the service above is running and all ports being listened to.
DaprClient client = new DaprClientBuilder().build();
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
//Publishing messages
client.invokeService(daprRun.getAppName(), "messages", message.getBytes(), HttpExtension.POST).block();
System.out.println("Invoke method messages : " + message);
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d", i);
//Publishing messages
client.invokeService(daprRun.getAppName(), "messages", message.getBytes(), HttpExtension.POST).block();
System.out.println("Invoke method messages : " + message);
}
Map<Integer, String> messages = client.invokeService(daprRun.getAppName(), "messages", null,
HttpExtension.GET, Map.class).block();
assertEquals(10, messages.size());
client.invokeService(daprRun.getAppName(), "messages/1", null, HttpExtension.DELETE).block();
messages = client.invokeService(daprRun.getAppName(), "messages", null, HttpExtension.GET, Map.class).block();
assertEquals(9, messages.size());
client.invokeService(daprRun.getAppName(), "messages/2", "updated message".getBytes(), HttpExtension.PUT).block();
messages = client.invokeService(daprRun.getAppName(), "messages", null, HttpExtension.GET, Map.class).block();
assertEquals("updated message", messages.get("2"));
}
Map<Integer,String> messages = client.invokeService(daprRun.getAppName(), "messages", null,
HttpExtension.GET, Map.class).block();
assertEquals(10, messages.size());
client.invokeService(daprRun.getAppName(),"messages/1",null, HttpExtension.DELETE).block();
messages = client.invokeService(daprRun.getAppName(), "messages", null, HttpExtension.GET, Map.class).block();
assertEquals(9, messages.size());
client.invokeService(daprRun.getAppName(), "messages/2", "updated message".getBytes(), HttpExtension.PUT).block();
messages = client.invokeService(daprRun.getAppName(), "messages", null, HttpExtension.GET, Map.class).block();
assertEquals("updated message", messages.get("2"));
}
@Test
public void testInvokeWithObjects() {
DaprClient client = new DaprClientBuilder().build();
public void testInvokeWithObjects() throws IOException {
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
Person person = new Person();
person.setName(String.format("Name %d", i));
person.setLastName(String.format("Last Name %d", i));
person.setBirthDate(new Date());
//Publishing messages
client.invokeService(daprRun.getAppName(), "persons", person, HttpExtension.POST).block();
System.out.println("Invoke method persons with parameter : " + person);
}
for (int i = 0; i < NUM_MESSAGES; i++) {
Person person= new Person();
person.setName(String.format("Name %d", i));
person.setLastName(String.format("Last Name %d", i));
person.setBirthDate(new Date());
//Publishing messages
client.invokeService(daprRun.getAppName(), "persons", person, HttpExtension.POST).block();
System.out.println("Invoke method persons with parameter : " + person);
List<Person> persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
assertEquals(10, persons.size());
client.invokeService(daprRun.getAppName(), "persons/1", null, HttpExtension.DELETE).block();
persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
assertEquals(9, persons.size());
Person person = new Person();
person.setName("John");
person.setLastName("Smith");
person.setBirthDate(Calendar.getInstance().getTime());
client.invokeService(daprRun.getAppName(), "persons/2", person, HttpExtension.PUT).block();
persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
Person resultPerson = persons.get(1);
assertEquals("John", resultPerson.getName());
assertEquals("Smith", resultPerson.getLastName());
}
List<Person> persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
assertEquals(10, persons.size());
client.invokeService(daprRun.getAppName(),"persons/1",null, HttpExtension.DELETE).block();
persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
assertEquals(9, persons.size());
Person person= new Person();
person.setName("John");
person.setLastName("Smith");
person.setBirthDate(Calendar.getInstance().getTime());
client.invokeService(daprRun.getAppName(), "persons/2", person, HttpExtension.PUT).block();
persons = Arrays.asList(client.invokeService(daprRun.getAppName(), "persons", null, HttpExtension.GET, Person[].class).block());
Person resultPerson= persons.get(1);
assertEquals("John", resultPerson.getName());
assertEquals("Smith", resultPerson.getLastName());
}
}

View File

@ -27,97 +27,98 @@ import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class)
public class PubSubIT extends BaseIT {
//Number of messages to be sent: 10
private static final int NUM_MESSAGES = 10;
//The title of the topic to be used for publishing
private static final String TOPIC_NAME = "testingtopic";
private static final String ANOTHER_TOPIC_NAME = "anothertopic";
//Number of messages to be sent: 10
private static final int NUM_MESSAGES = 10;
/**
* Parameters for this test.
* Param #1: useGrpc.
* @return Collection of parameter tuples.
*/
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { { false }, { true } });
//The title of the topic to be used for publishing
private static final String TOPIC_NAME = "testingtopic";
private static final String ANOTHER_TOPIC_NAME = "anothertopic";
/**
* Parameters for this test.
* Param #1: useGrpc.
*
* @return Collection of parameter tuples.
*/
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{{false}, {true}});
}
@Parameterized.Parameter
public boolean useGrpc;
@Test
public void testPubSub() throws Exception {
System.out.println("Working Directory = " + System.getProperty("user.dir"));
final DaprRun daprRun = startDaprApp(
this.getClass().getSimpleName(),
SubscriberService.SUCCESS_MESSAGE,
SubscriberService.class,
true,
60000);
// At this point, it is guaranteed that the service above is running and all ports being listened to.
if (this.useGrpc) {
daprRun.switchToGRPC();
} else {
daprRun.switchToHTTP();
}
@Parameterized.Parameter
public boolean useGrpc;
// Send a batch of messages on one topic
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d on topic %s", i, TOPIC_NAME);
//Publishing messages
client.publishEvent(TOPIC_NAME, message).block();
System.out.println(String.format("Published message: '%s' to topic '%s'", message, TOPIC_NAME));
}
@Test
public void testPubSub() throws Exception {
System.out.println("Working Directory = " + System.getProperty("user.dir"));
// Send a batch of different messages on the other.
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d on topic %s", i, ANOTHER_TOPIC_NAME);
//Publishing messages
client.publishEvent(ANOTHER_TOPIC_NAME, message).block();
System.out.println(String.format("Published message: '%s' to topic '%s'", message, ANOTHER_TOPIC_NAME));
}
final DaprRun daprRun = startDaprApp(
this.getClass().getSimpleName(),
SubscriberService.SUCCESS_MESSAGE,
SubscriberService.class,
true,
60000);
// At this point, it is guaranteed that the service above is running and all ports being listened to.
if (this.useGrpc) {
daprRun.switchToGRPC();
} else {
daprRun.switchToHTTP();
}
//Publishing a single byte: Example of non-string based content published
client.publishEvent(
TOPIC_NAME,
new byte[]{1},
Collections.singletonMap("content-type", "application/octet-stream")).block();
System.out.println("Published one byte.");
// Send a batch of messages on one topic
DaprClient client = new DaprClientBuilder().build();
Thread.sleep(3000);
callWithRetry(() -> {
System.out.println("Checking results for topic " + TOPIC_NAME);
final List<String> messages = client.invokeService(daprRun.getAppName(), "messages/testingtopic", null, HttpExtension.GET, List.class).block();
assertEquals(11, messages.size());
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d on topic %s", i, TOPIC_NAME);
//Publishing messages
client.publishEvent(TOPIC_NAME, message).block();
System.out.println(String.format("Published message: '%s' to topic '%s'", message, TOPIC_NAME));
assertTrue(messages.contains(String.format("This is message #%d on topic %s", i, TOPIC_NAME)));
}
// Send a batch of different messages on the other.
boolean foundByte = false;
for (String message : messages) {
if ((message.getBytes().length == 1) && (message.getBytes()[0] == 1)) {
foundByte = true;
}
}
assertTrue(foundByte);
}, 2000);
callWithRetry(() -> {
System.out.println("Checking results for topic " + ANOTHER_TOPIC_NAME);
final List<String> messages = client.invokeService(daprRun.getAppName(), "messages/anothertopic", null, HttpExtension.GET, List.class).block();
assertEquals(10, messages.size());
for (int i = 0; i < NUM_MESSAGES; i++) {
String message = String.format("This is message #%d on topic %s", i, ANOTHER_TOPIC_NAME);
//Publishing messages
client.publishEvent(ANOTHER_TOPIC_NAME, message).block();
System.out.println(String.format("Published message: '%s' to topic '%s'", message, ANOTHER_TOPIC_NAME));
assertTrue(messages.contains(String.format("This is message #%d on topic %s", i, ANOTHER_TOPIC_NAME)));
}
//Publishing a single byte: Example of non-string based content published
client.publishEvent(
TOPIC_NAME,
new byte[] { 1 },
Collections.singletonMap("content-type", "application/octet-stream")).block();
System.out.println("Published one byte.");
Thread.sleep(3000);
callWithRetry(() -> {
System.out.println("Checking results for topic " + TOPIC_NAME);
final List<String> messages = client.invokeService(daprRun.getAppName(), "messages/testingtopic", null, HttpExtension.GET, List.class).block();
assertEquals(11, messages.size());
for (int i = 0; i < NUM_MESSAGES; i++) {
assertTrue(messages.contains(String.format("This is message #%d on topic %s", i, TOPIC_NAME)));
}
boolean foundByte = false;
for (String message : messages) {
if ((message.getBytes().length == 1) && (message.getBytes()[0] == 1)) {
foundByte = true;
}
}
assertTrue(foundByte);
}, 2000);
callWithRetry(() -> {
System.out.println("Checking results for topic " + ANOTHER_TOPIC_NAME);
final List<String> messages = client.invokeService(daprRun.getAppName(), "messages/anothertopic", null, HttpExtension.GET, List.class).block();
assertEquals(10, messages.size());
for (int i = 0; i < NUM_MESSAGES; i++) {
assertTrue(messages.contains(String.format("This is message #%d on topic %s", i, ANOTHER_TOPIC_NAME)));
}
}, 2000);
}, 2000);
}
}
}

View File

@ -14,12 +14,14 @@ import io.dapr.client.DaprClientHttp;
import io.dapr.it.BaseIT;
import io.dapr.it.DaprRun;
import io.dapr.it.services.EmptyService;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.IOException;
import java.util.*;
import static org.junit.Assert.*;
@ -96,6 +98,11 @@ public class SecretsClientIT extends BaseIT {
}
}
@After
public void tearDown() throws IOException {
daprClient.close();
}
@Test
public void getSecret() throws Exception {
String key = UUID.randomUUID().toString();

View File

@ -13,11 +13,13 @@ import io.dapr.client.domain.StateOptions;
import io.dapr.it.BaseIT;
import io.dapr.it.DaprRun;
import io.dapr.it.services.EmptyService;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.time.Duration;
import static org.junit.Assert.*;
@ -46,6 +48,11 @@ public class GRPCStateClientIT extends BaseIT {
assertTrue(daprClient instanceof DaprClientGrpc);
}
@AfterClass
public static void tearDown() throws IOException {
daprClient.close();
}
@Test
public void saveAndGetState() {

View File

@ -64,5 +64,6 @@ public class HelloWorldClientIT extends BaseIT {
System.out.println("Got: " + value);
Assert.assertEquals("", value);
}
channel.shutdown();
}
}

View File

@ -11,6 +11,7 @@ import io.dapr.client.domain.Verb;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.Closeable;
import java.util.List;
import java.util.Map;
@ -19,7 +20,7 @@ import java.util.Map;
*
* @see io.dapr.client.DaprClientBuilder for information on how to make instance for this interface.
*/
public interface DaprClient {
public interface DaprClient extends Closeable {
/**
* Publish an event.

View File

@ -14,10 +14,13 @@ import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import okhttp3.OkHttpClient;
import java.io.Closeable;
/**
* A builder for the DaprClient,
* Currently only and HTTP Client will be supported.
*/
public class DaprClientBuilder {
/**
@ -105,7 +108,13 @@ public class DaprClientBuilder {
throw new IllegalStateException("Invalid port.");
}
ManagedChannel channel = ManagedChannelBuilder.forAddress(Constants.DEFAULT_HOSTNAME, port).usePlaintext().build();
return new DaprClientGrpc(DaprGrpc.newFutureStub(channel), this.objectSerializer, this.stateSerializer);
Closeable closeableChannel = () -> {
if (channel != null && !channel.isShutdown()) {
channel.shutdown();
}
};
DaprGrpc.DaprFutureStub stub = DaprGrpc.newFutureStub(channel);
return new DaprClientGrpc(closeableChannel, stub, this.objectSerializer, this.stateSerializer);
}
/**

View File

@ -19,6 +19,7 @@ import io.dapr.v1.DaprGrpc;
import io.dapr.v1.DaprProtos;
import reactor.core.publisher.Mono;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@ -34,6 +35,11 @@ import static io.dapr.client.domain.StateOptions.RetryPolicy;
*/
public class DaprClientGrpc implements DaprClient {
/**
* The GRPC managed channel to be used.
*/
private Closeable channel;
/**
* The GRPC client to be used.
*
@ -54,15 +60,18 @@ public class DaprClientGrpc implements DaprClient {
/**
* Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder
*
* @param closeableChannel A closeable for a Managed GRPC channel
* @param futureClient GRPC client
* @param objectSerializer Serializer for transient request/response objects.
* @param stateSerializer Serializer for state objects.
* @see DaprClientBuilder
*/
DaprClientGrpc(
Closeable closeableChannel,
DaprGrpc.DaprFutureStub futureClient,
DaprObjectSerializer objectSerializer,
DaprObjectSerializer stateSerializer) {
this.channel = closeableChannel;
this.client = futureClient;
this.objectSerializer = objectSerializer;
this.stateSerializer = stateSerializer;
@ -642,4 +651,15 @@ public class DaprClientGrpc implements DaprClient {
return this.getSecret(secretStoreName, secretName, null);
}
/**
* Closes the ManagedChannel for GRPC.
* @see io.grpc.ManagedChannel#shutdown()
* @throws IOException on exception.
*/
@Override
public void close() throws IOException {
if (channel != null) {
channel.close();
}
}
}

View File

@ -592,4 +592,8 @@ public class DaprClientHttp implements DaprClient {
return this.getSecret(secretStoreName, secretName, null);
}
@Override
public void close() throws IOException {
client.close();
}
}

View File

@ -16,6 +16,7 @@ import okhttp3.Request;
import okhttp3.RequestBody;
import reactor.core.publisher.Mono;
import java.io.Closeable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@ -25,7 +26,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class DaprHttp {
public class DaprHttp implements Closeable {
/**
* Dapr's http default scheme.
*/
@ -239,4 +240,13 @@ public class DaprHttp {
return OBJECT_MAPPER.readValue(json, DaprError.class);
}
/**
* Shutdown call is not necessary for OkHttpClient.
* @see OkHttpClient
*/
@Override
public void close() throws IOException {
// No code needed
}
}

View File

@ -12,18 +12,19 @@ import com.google.protobuf.ByteString;
import com.google.protobuf.Empty;
import io.dapr.client.domain.State;
import io.dapr.client.domain.StateOptions;
import io.dapr.client.domain.Verb;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.TypeRef;
import io.dapr.v1.CommonProtos;
import io.dapr.v1.DaprGrpc;
import io.dapr.v1.DaprProtos;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import reactor.core.publisher.Mono;
import java.io.Closeable;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
@ -43,15 +44,25 @@ public class DaprClientGrpcTest {
private static final String SECRET_STORE_NAME = "MySecretStore";
private Closeable closeable;
private DaprGrpc.DaprFutureStub client;
private DaprClientGrpc adapter;
private ObjectSerializer serializer;
@Before
public void setup() {
public void setup() throws IOException {
closeable = mock(Closeable.class);
client = mock(DaprGrpc.DaprFutureStub.class);
adapter = new DaprClientGrpc(client, new DefaultObjectSerializer(), new DefaultObjectSerializer());
adapter = new DaprClientGrpc(closeable, client, new DefaultObjectSerializer(), new DefaultObjectSerializer());
serializer = new ObjectSerializer();
doNothing().when(closeable).close();
}
@After
public void tearDown() throws IOException {
adapter.close();
verify(closeable).close();
verifyNoMoreInteractions(closeable);
}
@Test(expected = RuntimeException.class)

View File

@ -434,6 +434,14 @@ public class DaprClientHttpTest {
assertNull(mono.block());
}
@Test(expected = IllegalArgumentException.class)
public void saveStateNullStateStoreName() {
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Void> mono = daprClientHttp.saveStates(null, null);
assertNull(mono.block());
}
@Test
public void saveStatesNull() {
State<String> stateKeyValue = new State("value", "key", "", null);
@ -621,6 +629,13 @@ public class DaprClientHttpTest {
});
}
@Test(expected = IllegalArgumentException.class)
public void getSecretsNullStoreName() {
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
daprClientHttp.getSecret(null, "key").block();
}
@Test
public void getSecretsWithMetadata() {
mockInterceptor.addRule()