This sample illustrates the capabilities provided by Dapr Java SDK for state management. For further information about state management please refer to this link
- Dapr CLI.
- Java JDK 11 (or greater):
- Apache Maven version 3.x.
Clone this repository:
git clone https://github.com/dapr/java-sdk.git
cd java-sdk
Then build the Maven project:
# make sure you are in the `java-sdk` directory.
mvn install
Then change into the examples
directory:
cd examples
Run dapr init
to initialize Dapr in Self-Hosted Mode if it's not already initialized.
docker compose -f ./src/main/java/io/dapr/examples/state/docker-compose-single-mongo.yml up -d
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:
public class StateClient {
///...
private static final String STATE_STORE_NAME = "statestore";
private static final String FIRST_KEY_NAME = "myKey";
private static final String SECOND_KEY_NAME = "myKey2";
///...
public static void main(String[] args) throws Exception {
try (DaprClient client = new DaprClientBuilder().build()) {
System.out.println("Waiting for Dapr sidecar ...");
client.waitForSidecar(10000).block();
System.out.println("Dapr sidecar is ready.");
String message = args.length == 0 ? " " : args[0];
MyClass myClass = new MyClass();
myClass.message = message;
MyClass secondState = new MyClass();
secondState.message = "test message";
client.saveState(STATE_STORE_NAME, FIRST_KEY_NAME, myClass).block();
System.out.println("Saving class with message: " + message);
Mono<State<MyClass>> retrievedMessageMono = client.getState(STATE_STORE_NAME, FIRST_KEY_NAME, MyClass.class);
System.out.println("Retrieved class message from state: " + (retrievedMessageMono.block().getValue()).message);
System.out.println("Updating previous state and adding another state 'test state'... ");
myClass.message = message + " updated";
System.out.println("Saving updated class with message: " + myClass.message);
// execute transaction
List<TransactionalStateOperation<?>> operationList = new ArrayList<>();
operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT,
new State<>(FIRST_KEY_NAME, myClass, "")));
operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT,
new State<>(SECOND_KEY_NAME, secondState, "")));
client.executeStateTransaction(STATE_STORE_NAME, operationList).block();
// get multiple states
Mono<List<State<MyClass>>> retrievedMessagesMono = client.getStates(STATE_STORE_NAME,
Arrays.asList(FIRST_KEY_NAME, SECOND_KEY_NAME), MyClass.class);
System.out.println("Retrieved messages using bulk get:");
retrievedMessagesMono.block().forEach(System.out::println);
System.out.println("Deleting states...");
System.out.println("Verify delete key request is aborted if an etag different from stored is passed.");
// delete state API
try {
client.deleteState(STATE_STORE_NAME, FIRST_KEY_NAME, "100", null).block();
} catch (DaprException ex) {
if (ex.getErrorCode().equals(Status.Code.ABORTED.toString())) {
// Expected error due to etag mismatch.
System.out.println(String.format("Expected failure. %s ", ex.getMessage()));
} else {
System.out.println("Unexpected exception.");
throw ex;
}
}
System.out.println("Trying to delete again with correct etag.");
String storedEtag = client.getState(STATE_STORE_NAME, FIRST_KEY_NAME, MyClass.class).block().getEtag();
client.deleteState(STATE_STORE_NAME, FIRST_KEY_NAME, storedEtag, null).block();
// Delete operation using transaction API
operationList.clear();
operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE,
new State<>(SECOND_KEY_NAME)));
client.executeStateTransaction(STATE_STORE_NAME, operationList).block();
Mono<List<State<MyClass>>> retrievedDeletedMessageMono = client.getStates(STATE_STORE_NAME,
Arrays.asList(FIRST_KEY_NAME, SECOND_KEY_NAME), MyClass.class);
System.out.println("Trying to retrieve deleted states: ");
retrievedDeletedMessageMono.block().forEach(System.out::println);
// 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 received objects, and stateSerializer
is for objects to be persisted.
This example performs multiple operations:
client.waitForSidecar(...)
for waiting until Dapr sidecar is ready.client.saveState(...)
for persisting an instance ofMyClass
.client.getState(...)
operation in order to retrieve back the persisted state using the same key.client.executeStateTransaction(...)
operation in order to update existing state and add new state.client.getBulkState(...)
operation in order to retrieve back the persisted states using the same keys.client.deleteState(...)
operation to remove one of the persisted states. An example of etag mismatch error is if something other than the current etag is added to request.client.executeStateTransaction(...)
operation in order to remove the other persisted state.
Finally, the code tries to retrieve the deleted states, which should not be found.
The Dapr client is also within a try-with-resource block to properly close the client at the end.
Run this example with the following command:
dapr run --resources-path ./components/state --app-id state-example -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.state.StateClient 'my message'
Once running, the StateClient should print the output as follows:
== APP == Waiting for Dapr sidecar ...
== APP == Dapr sidecar is ready.
== APP == Saving class with message: my message
== APP == Retrieved class message from state: my message
== APP == Updating previous state and adding another state 'test state'...
== APP == Saving updated class with message: my message updated
== APP == Retrieved messages using bulk get:
== APP == StateKeyValue{key='myKey', value=my message updated, etag='2', metadata={'{}'}, error='null', options={'null'}}
== APP == StateKeyValue{key='myKey2', value=test message, etag='1', metadata={'{}'}, error='null', options={'null'}}
== APP == Deleting states...
== APP == Verify delete key request is aborted if an etag different from stored is passed.
== APP == Expected failure. ABORTED: failed deleting state with key myKey: possible etag mismatch. error from state store: ERR Error running script (call to f_9b5da7354cb61e2ca9faff50f6c43b81c73c0b94): @user_script:1: user_script:1: failed to delete Tailmad-Fang||myKey
== APP == Trying to delete again with correct etag.
== APP == Trying to retrieve deleted states:
== APP == StateKeyValue{key='myKey', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
== APP == StateKeyValue{key='myKey2', value=null, etag='null', metadata={'{}'}, error='null', options={'null'}}
== APP == Done
To close the app either press CTRL+C
or run
dapr stop --app-id state-example
Then, stop MongoDB container.
docker compose -f ./src/main/java/io/dapr/examples/state/docker-compose-single-mongo.yml down