diff --git a/examples/pom.xml b/examples/pom.xml index e833af8740..400b9e1df2 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -34,6 +34,11 @@ commons-cli 1.4 + + commons-io + commons-io + 2.6 + org.json json diff --git a/examples/src/main/java/io/dapr/examples/actors/http/DemoActor.java b/examples/src/main/java/io/dapr/examples/actors/http/DemoActor.java index 95d56a1720..30546ea1c3 100644 --- a/examples/src/main/java/io/dapr/examples/actors/http/DemoActor.java +++ b/examples/src/main/java/io/dapr/examples/actors/http/DemoActor.java @@ -10,5 +10,9 @@ */ public interface DemoActor { + void registerReminder(); + String say(String something); + + void clock(String message); } diff --git a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorClient.java b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorClient.java index 0639537756..5faebb051f 100644 --- a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorClient.java +++ b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorClient.java @@ -8,7 +8,6 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyBuilder; -import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; @@ -53,12 +52,13 @@ public static void main(String[] args) throws Exception { private static final CompletableFuture callActorNTimes(ActorProxy actor) { return CompletableFuture.runAsync(() -> { + actor.invokeActorMethod("registerReminder").block(); for (int i = 0; i < NUM_MESSAGES_PER_ACTOR; i++) { String result = actor.invokeActorMethod(METHOD_NAME, - String.format("Actor %s said message #%d", actor.getActorId().toString(), i)).block(); + String.format("Actor %s said message #%d", actor.getActorId().toString(), i), String.class).block(); System.out.println(String.format("Actor %s got a reply: %s", actor.getActorId().toString(), result)); try { - Thread.sleep(1000); + Thread.sleep((long)(1000 * Math.random())); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); diff --git a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorImpl.java b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorImpl.java index bade8db1e9..82d8555d01 100644 --- a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorImpl.java +++ b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorImpl.java @@ -6,13 +6,12 @@ package io.dapr.examples.actors.http; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.AbstractActor; -import io.dapr.actors.runtime.Actor; -import io.dapr.actors.runtime.ActorRuntimeContext; -import io.dapr.actors.runtime.ActorType; +import io.dapr.actors.runtime.*; +import reactor.core.publisher.Mono; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.Duration; import java.util.Calendar; import java.util.TimeZone; @@ -20,7 +19,7 @@ * Implementation of the DemoActor for the server side. */ @ActorType(Name = "DemoActor") -public class DemoActorImpl extends AbstractActor implements DemoActor, Actor { +public class DemoActorImpl extends AbstractActor implements DemoActor, Actor, Remindable { /** * Format to output date and time. @@ -29,6 +28,22 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Actor { public DemoActorImpl(ActorRuntimeContext runtimeContext, ActorId id) { super(runtimeContext, id); + + super.registerActorTimer( + null, + "clock", + "ping!", + Duration.ofSeconds(2), + Duration.ofSeconds(1)); + } + + @Override + public void registerReminder() { + super.registerReminder( + "myremind", + (int)(Integer.MAX_VALUE * Math.random()), + Duration.ofSeconds(5), + Duration.ofSeconds(2)); } @Override @@ -37,9 +52,39 @@ public String say(String something) { String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); // Handles the request by printing message. - System.out.println("Server: " + something == null ? "" : something + " @ " + utcNowAsString); + System.out.println("Server say method for actor " + + super.getId() + ": " + + (something == null ? "" : something + " @ " + utcNowAsString)); // Now respond with current timestamp. return utcNowAsString; } + + @Override + public void clock(String message) { + Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); + + // Handles the request by printing message. + System.out.println("Server timer for actor " + + super.getId() + ": " + + (message == null ? "" : message + " @ " + utcNowAsString)); + } + + @Override + public Class getStateType() { + return Integer.class; + } + + @Override + public Mono receiveReminder(String reminderName, Integer state, Duration dueTime, Duration period) { + Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); + + // Handles the request by printing message. + System.out.println(String.format( + "Server reminded actor %s of: %s for %d @ %s", + this.getId(), reminderName, state, utcNowAsString)); + return Mono.empty(); + } } diff --git a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java index 35363de035..a5d39240c9 100644 --- a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java +++ b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java @@ -19,6 +19,7 @@ import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; +import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; @@ -46,7 +47,9 @@ public class DemoActorService { .get("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/dapr/config", DemoActorService::handleDaprConfig) .post("/actors/{actorType}/{id}", DemoActorService::handleActorActivate) .delete("/actors/{actorType}/{id}", DemoActorService::handleActorDeactivate) - .put("/actors/{actorType}/{id}/method/{methodName}", DemoActorService::handleActorInvoke); + .put("/actors/{actorType}/{id}/method/{methodName}", DemoActorService::handleActorInvoke) + .put("/actors/{actorType}/{id}/method/timer/{timerName}", DemoActorService::handleActorTimer) + .put("/actors/{actorType}/{id}/method/remind/{reminderName}", DemoActorService::handleActorReminder); private final int port; @@ -135,11 +138,39 @@ private static void handleActorInvoke(HttpServerExchange exchange) throws IOExce String actorId = findParamValueOrNull(exchange, "id"); String methodName = findParamValueOrNull(exchange, "methodName"); exchange.startBlocking(); - String data = findData(exchange.getInputStream()); + String data = findMethodData(exchange.getInputStream()); String result = ActorRuntime.getInstance().invoke(actorType, actorId, methodName, data).block(); exchange.getResponseSender().send(buildResponse(result)); } + private static void handleActorTimer(HttpServerExchange exchange) throws IOException { + if (exchange.isInIoThread()) { + exchange.dispatch(DemoActorService::handleActorTimer); + return; + } + + String actorType = findParamValueOrNull(exchange, "actorType"); + String actorId = findParamValueOrNull(exchange, "id"); + String timerName = findParamValueOrNull(exchange, "timerName"); + ActorRuntime.getInstance().invokeTimer(actorType, actorId, timerName).block(); + exchange.getResponseSender().send(""); + } + + private static void handleActorReminder(HttpServerExchange exchange) throws IOException { + if (exchange.isInIoThread()) { + exchange.dispatch(DemoActorService::handleActorReminder); + return; + } + + String actorType = findParamValueOrNull(exchange, "actorType"); + String actorId = findParamValueOrNull(exchange, "id"); + String reminderName = findParamValueOrNull(exchange, "reminderName"); + exchange.startBlocking(); + String params = IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8); + ActorRuntime.getInstance().invokeReminder(actorType, actorId, reminderName, params).block(); + exchange.getResponseSender().send(""); + } + private static String findParamValueOrNull(HttpServerExchange exchange, String name) { Map> params = exchange.getQueryParameters(); if (params == null) { @@ -154,7 +185,7 @@ private static String findParamValueOrNull(HttpServerExchange exchange, String n return values.getFirst(); } - private static String findData(InputStream stream) throws IOException { + private static String findMethodData(InputStream stream) throws IOException { JsonNode root = OBJECT_MAPPER.readTree(stream); if (root == null) { return null; diff --git a/sdk/src/main/java/io/dapr/actors/ActorId.java b/sdk/src/main/java/io/dapr/actors/ActorId.java index aa1cf50381..77a5f235b3 100644 --- a/sdk/src/main/java/io/dapr/actors/ActorId.java +++ b/sdk/src/main/java/io/dapr/actors/ActorId.java @@ -11,131 +11,131 @@ */ public class ActorId extends Object implements Comparable { - /** - * The ID of the actor as a String. - */ - private final String stringId; - - /** - * An error message for an invalid constructor arg. - */ - private final String errorMsg = "actor needs to be initialized with an id!"; - - /** - * Initializes a new instance of the ActorId class with the id passed in. - * - * @param id Value for actor id - */ - public ActorId(String id) { - if (id != null) { - this.stringId = id; - } else { - throw new IllegalArgumentException(errorMsg); + /** + * The ID of the actor as a String. + */ + private final String stringId; + + /** + * An error message for an invalid constructor arg. + */ + private final String errorMsg = "actor needs to be initialized with an id!"; + + /** + * Initializes a new instance of the ActorId class with the id passed in. + * + * @param id Value for actor id + */ + public ActorId(String id) { + if (id != null) { + this.stringId = id; + } else { + throw new IllegalArgumentException(errorMsg); + } } - } - - /** - * - * @return The String representation of this ActorId - */ - @Override - public String toString() { - return this.stringId; - } - - /** - * Compares this instance with a specified {link #ActorId} object and - * indicates whether this instance precedes, follows, or appears in the same - * position in the sort order as the specified actorId. - *

- * The comparison is done based on the id if both the instances. - * - * @param other The actorId to compare with this instance. - * @return A 32-bit signed integer that indicates whether this instance - * precedes, follows, or appears in the same position in the sort order as the - * other parameter. - */ - @Override - public int compareTo(ActorId other) { - return (other == null) ? 1 - : compareContent(this, other); - } - - /** - * - * @return The hash code of this ActorId - */ - @Override - public int hashCode() { - return this.stringId.hashCode(); - } - - /** - * Compare if the content of two ids are the same. - * @param id1 One identifier. - * @param id2 Another identifier. - * @return -1, 0, or 1 depending on the compare result of the stringId member. - */ - private int compareContent(ActorId id1, ActorId id2) { - return id1.stringId.compareTo(id2.stringId); - } - - /** - * Checks if this instance is equals to the other instance. - * @return true if the 2 ActorId's are equal. - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; + + /** + * @return The String representation of this ActorId + */ + @Override + public String toString() { + return this.stringId; + } + + /** + * Compares this instance with a specified {link #ActorId} object and + * indicates whether this instance precedes, follows, or appears in the same + * position in the sort order as the specified actorId. + *

+ * The comparison is done based on the id if both the instances. + * + * @param other The actorId to compare with this instance. + * @return A 32-bit signed integer that indicates whether this instance + * precedes, follows, or appears in the same position in the sort order as the + * other parameter. + */ + @Override + public int compareTo(ActorId other) { + return (other == null) ? 1 + : compareContent(this, other); + } + + /** + * @return The hash code of this ActorId + */ + @Override + public int hashCode() { + return this.stringId.hashCode(); + } + + /** + * Compare if the content of two ids are the same. + * + * @param id1 One identifier. + * @param id2 Another identifier. + * @return -1, 0, or 1 depending on the compare result of the stringId member. + */ + private int compareContent(ActorId id1, ActorId id2) { + return id1.stringId.compareTo(id2.stringId); + } + + /** + * Checks if this instance is equals to the other instance. + * + * @return true if the 2 ActorId's are equal. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + return hasEqualContent(this, (ActorId) obj); } - if (obj == null) { - return false; + /** + * Creates a new ActorId with a random id. + * + * @return A new ActorId with a random id. + */ + public static ActorId createRandom() { + UUID id = UUID.randomUUID(); + return new ActorId(id.toString()); } - if (getClass() != obj.getClass()) { - return false; + /** + * Determines whether two specified actorIds have the same id. + * + * @param id1 The first actorId to compare, or null + * @param id2 The second actorId to compare, or null. + * @return true if the id is same for both objects; otherwise, false. + */ + private static boolean equals(ActorId id1, ActorId id2) { + if (id1 == null && id2 == null) { + return true; + } else if (id2 == null || id1 == null) { + return false; + } else { + return hasEqualContent(id1, id2); + } } - return hasEqualContent(this, (ActorId) obj); - } - - /** - * Creates a new ActorId with a random id. - * - * @return A new ActorId with a random id. - */ - public static ActorId createRandom() { - UUID id = UUID.randomUUID(); - return new ActorId(id.toString()); - } - - /** - * Determines whether two specified actorIds have the same id. - * - * @param id1 The first actorId to compare, or null - * @param id2 The second actorId to compare, or null. - * @return true if the id is same for both objects; otherwise, false. - */ - private static boolean equals(ActorId id1, ActorId id2) { - if (id1 == null && id2 == null) { - return true; - } else if (id2 == null || id1 == null) { - return false; - } else { - return hasEqualContent(id1, id2); + /** + * Compares if two actors have the same content. + * + * @param id1 One identifier. + * @param id2 Another identifier. + * @return true if the two ActorId's are equal + */ + private static boolean hasEqualContent(ActorId id1, ActorId id2) { + return id1.stringId.equals(id2.stringId); } - } - - /** - * Compares if two actors have the same content. - * - * @param id1 One identifier. - * @param id2 Another identifier. - * @return true if the two ActorId's are equal - */ - private static boolean hasEqualContent(ActorId id1, ActorId id2) { - return id1.stringId.equals(id2.stringId); - } } diff --git a/sdk/src/main/java/io/dapr/actors/ActorTrace.java b/sdk/src/main/java/io/dapr/actors/ActorTrace.java index 487579e1af..d93f8ad463 100644 --- a/sdk/src/main/java/io/dapr/actors/ActorTrace.java +++ b/sdk/src/main/java/io/dapr/actors/ActorTrace.java @@ -16,71 +16,76 @@ */ public final class ActorTrace { - /** - * Gets the default Logger. - */ - private static final Logger LOGGER = Logger.getLogger(ActorTrace.class.getName()); + /** + * Gets the default Logger. + */ + private static final Logger LOGGER = Logger.getLogger(ActorTrace.class.getName()); - /** - * Writes an information trace log. - * @param type Type of log. - * @param id Instance identifier. - * @param msgFormat Message or message format (with type and id input as well). - * @param params Params for the message. - */ - public void writeInfo(String type, String id, String msgFormat, Object... params) { - this.write(Level.INFO, type, id, msgFormat, params); - } - - /** - * Writes an warning trace log. - * @param type Type of log. - * @param id Instance identifier. - * @param msgFormat Message or message format (with type and id input as well). - * @param params Params for the message. - */ - public void writeWarning(String type, String id, String msgFormat, Object... params) { - this.write(Level.WARNING, type, id, msgFormat, params); - } + /** + * Writes an information trace log. + * + * @param type Type of log. + * @param id Instance identifier. + * @param msgFormat Message or message format (with type and id input as well). + * @param params Params for the message. + */ + public void writeInfo(String type, String id, String msgFormat, Object... params) { + this.write(Level.INFO, type, id, msgFormat, params); + } - /** - * Writes an error trace log. - * @param type Type of log. - * @param id Instance identifier. - * @param msgFormat Message or message format (with type and id input as well). - * @param params Params for the message. - */ - public void writeError(String type, String id, String msgFormat, Object... params) { - this.write(Level.SEVERE, type, id, msgFormat, params); - } + /** + * Writes an warning trace log. + * + * @param type Type of log. + * @param id Instance identifier. + * @param msgFormat Message or message format (with type and id input as well). + * @param params Params for the message. + */ + public void writeWarning(String type, String id, String msgFormat, Object... params) { + this.write(Level.WARNING, type, id, msgFormat, params); + } - /** - * Writes a trace log. - * @param level Severity level of the log. - * @param type Type of log. - * @param id Instance identifier. - * @param msgFormat Message or message format (with type and id input as well). - * @param params Params for the message. - */ - private void write(Level level, String type, String id, String msgFormat, Object... params) { - String formatString = String.format("%s:%s %s", emptyIfNul(type), emptyIfNul(id), emptyIfNul(msgFormat)); - if ((params == null) || (params.length == 0)) { - LOGGER.log(level, formatString); - } else { - LOGGER.log(level, String.format(formatString, params)); + /** + * Writes an error trace log. + * + * @param type Type of log. + * @param id Instance identifier. + * @param msgFormat Message or message format (with type and id input as well). + * @param params Params for the message. + */ + public void writeError(String type, String id, String msgFormat, Object... params) { + this.write(Level.SEVERE, type, id, msgFormat, params); } - } - /** - * Utility method that returns empty if String is null. - * @param s String to be checked. - * @return String (if not null) or empty (if null). - */ - private static String emptyIfNul(String s) { - if (s == null) { - return ""; + /** + * Writes a trace log. + * + * @param level Severity level of the log. + * @param type Type of log. + * @param id Instance identifier. + * @param msgFormat Message or message format (with type and id input as well). + * @param params Params for the message. + */ + private void write(Level level, String type, String id, String msgFormat, Object... params) { + String formatString = String.format("%s:%s %s", emptyIfNul(type), emptyIfNul(id), emptyIfNul(msgFormat)); + if ((params == null) || (params.length == 0)) { + LOGGER.log(level, formatString); + } else { + LOGGER.log(level, String.format(formatString, params)); + } } - return s; - } + /** + * Utility method that returns empty if String is null. + * + * @param s String to be checked. + * @return String (if not null) or empty (if null). + */ + private static String emptyIfNul(String s) { + if (s == null) { + return ""; + } + + return s; + } } diff --git a/sdk/src/main/java/io/dapr/actors/Constants.java b/sdk/src/main/java/io/dapr/actors/Constants.java deleted file mode 100644 index d0782de0ee..0000000000 --- a/sdk/src/main/java/io/dapr/actors/Constants.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ -package io.dapr.actors; - -/** - * Useful constants for the Dapr's Actor SDK. - */ -public final class Constants { - - /** - * Dapr API used in this client. - */ - public static final String API_VERSION = "v1.0"; - - /** - * Dapr's default hostname. - */ - public static final String DEFAULT_HOSTNAME = "localhost"; - - /** - * Dapr's default port. - */ - public static final int DEFAULT_PORT = 3500; - - /** - * Environment variable used to set Dapr's port. - */ - public static final String ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; - - /** - * Header used for request id in Dapr. - */ - public static final String HEADER_DAPR_REQUEST_ID = "X-DaprRequestId"; - - /** - * Base URL for Dapr Actor APIs. - */ - private static String ACTORS_BASE_URL = API_VERSION + "/" + "actors"; - - /** - * String format for Actors state management relative url. - */ - public static String ACTOR_STATE_KEY_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/state/%s"; - - /** - * String format for Actors state management relative url. - */ - public static String ACTOR_STATE_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/state"; - - /** - * String format for Actors method invocation relative url. - */ - public static String ACTOR_METHOD_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/method/%s"; - - /** - * String format for Actors reminder registration relative url.. - */ - public static String ACTOR_REMINDER_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/reminders/%s"; - - /** - * String format for Actors timer registration relative url.. - */ - public static String ACTOR_TIMER_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/reminders/%s"; - - /** - * Invoke Publish Path - */ - public static String PUBLISH_PATH = API_VERSION + "/publish"; - - /** - * Invoke Binding Path - */ - public static String BINDING_PATH = API_VERSION + "/binding"; - - /** - * State Path - */ - public static String STATE_PATH = API_VERSION + "/state"; - - - -} diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorMethodEnvelope.java b/sdk/src/main/java/io/dapr/actors/client/ActorMethodEnvelope.java index 857f8f31d4..693708a22b 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorMethodEnvelope.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorMethodEnvelope.java @@ -10,24 +10,26 @@ */ public class ActorMethodEnvelope { - /** - * Data serialized for input/output of Actor methods. - */ - private byte[] data; + /** + * Data serialized for input/output of Actor methods. + */ + private byte[] data; - /** - * Gets the data serialized for input/output of Actor methods. - * @return Data serialized for input/output of Actor methods. - */ - public byte[] getData() { - return data; - } + /** + * Gets the data serialized for input/output of Actor methods. + * + * @return Data serialized for input/output of Actor methods. + */ + public byte[] getData() { + return data; + } - /** - * Sets the data serialized for input/output of Actor methods. - * @param data Data serialized for input/output of Actor methods. - */ - public void setData(byte[] data) { - this.data = data; - } + /** + * Sets the data serialized for input/output of Actor methods. + * + * @param data Data serialized for input/output of Actor methods. + */ + public void setData(byte[] data) { + this.data = data; + } } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxy.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxy.java index 6f35b3429d..c012ff49b6 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxy.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxy.java @@ -3,8 +3,6 @@ import io.dapr.actors.ActorId; import reactor.core.publisher.Mono; -import java.io.IOException; - /** * Proxy to communicate to a given Actor instance in Dapr. */ @@ -28,7 +26,8 @@ public interface ActorProxy { * Invokes an Actor method on Dapr. * * @param methodName Method name to invoke. - * @param clazz The type of the return class. + * @param clazz The type of the return class. + * @param The type to be returned. * @return Asynchronous result with the Actor's response. */ Mono invokeActorMethod(String methodName, Class clazz); @@ -37,11 +36,12 @@ public interface ActorProxy { * Invokes an Actor method on Dapr. * * @param methodName Method name to invoke. - * @param data Object with the data. - * @param clazz The type of the return class. + * @param data Object with the data. + * @param clazz The type of the return class. + * @param The type to be returned. * @return Asynchronous result with the Actor's response. */ - Mono invokeActorMethod(String methodName, Object data, Class clazz); + Mono invokeActorMethod(String methodName, Object data, Class clazz); /** * Invokes an Actor method on Dapr. @@ -49,15 +49,15 @@ public interface ActorProxy { * @param methodName Method name to invoke. * @return Asynchronous result with the Actor's response. */ - Mono invokeActorMethod(String methodName); + Mono invokeActorMethod(String methodName); /** * Invokes an Actor method on Dapr. * * @param methodName Method name to invoke. - * @param data Object with the data. + * @param data Object with the data. * @return Asynchronous result with the Actor's response. */ - Mono invokeActorMethod(String methodName, Object data); + Mono invokeActorMethod(String methodName, Object data); } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java index b86e3d6950..e2bfec6af6 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java @@ -11,14 +11,14 @@ */ interface ActorProxyAsyncClient { - /** - * Invokes an Actor method on Dapr. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param methodName Method name to invoke. - * @param jsonPayload Serialized body. - * @return Asynchronous result with the Actor's response. - */ - Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload); + /** + * Invokes an Actor method on Dapr. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param methodName Method name to invoke. + * @param jsonPayload Serialized body. + * @return Asynchronous result with the Actor's response. + */ + Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload); } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java index 244807adb5..5fb0e887d7 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java @@ -1,85 +1,86 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.utils.ObjectSerializer; +import io.dapr.actors.runtime.ActorStateSerializer; /** * Builder to generate an ActorProxy instance. */ public class ActorProxyBuilder { - /** - * Serializer for content to be sent back and forth between actors. - */ - private static final ObjectSerializer SERIALIZER = new ObjectSerializer(); + /** + * Serializer for content to be sent back and forth between actors. + */ + private static final ActorStateSerializer SERIALIZER = new ActorStateSerializer(); - /** - * Builder for the Dapr client. - */ - private final ActorProxyClientBuilder clientBuilder = new ActorProxyClientBuilder(); + /** + * Builder for the Dapr client. + */ + private final ActorProxyClientBuilder clientBuilder = new ActorProxyClientBuilder(); - /** - * Actor's type. - */ - private String actorType; + /** + * Actor's type. + */ + private String actorType; - /** - * Actor's identifier. - */ - private ActorId actorId; + /** + * Actor's identifier. + */ + private ActorId actorId; - /** - * Changes build config to use specific port. - * - * @param port Port to be used. - * @return Same builder object. - */ - public ActorProxyBuilder withPort(int port) { - this.clientBuilder.withPort(port); - return this; - } - - /** - * Changes build config to use given Actor's type. - * - * @param actorType Actor's type. - * @return Same builder object. - */ - public ActorProxyBuilder withActorType(String actorType) { - this.actorType = actorType; - return this; - } - - /** - * Changes build config to use given Actor's identifier. - * - * @param actorId Actor's identifier. - * @return Same builder object. - */ - public ActorProxyBuilder withActorId(ActorId actorId) { - this.actorId = actorId; - return this; - } + /** + * Changes build config to use specific port. + * + * @param port Port to be used. + * @return Same builder object. + */ + public ActorProxyBuilder withPort(int port) { + this.clientBuilder.withPort(port); + return this; + } - /** - * Instantiates a new ActorProxy. - * - * @return New instance of ActorProxy. - */ - public ActorProxy build() { - if ((this.actorType == null) || this.actorType.isEmpty()) { - throw new IllegalArgumentException("Cannot instantiate an Actor without type."); + /** + * Changes build config to use given Actor's type. + * + * @param actorType Actor's type. + * @return Same builder object. + */ + public ActorProxyBuilder withActorType(String actorType) { + this.actorType = actorType; + return this; } - if (this.actorId == null) { - throw new IllegalArgumentException("Cannot instantiate an Actor without Id."); + /** + * Changes build config to use given Actor's identifier. + * + * @param actorId Actor's identifier. + * @return Same builder object. + */ + public ActorProxyBuilder withActorId(ActorId actorId) { + this.actorId = actorId; + return this; } - return new ActorProxyImpl( - this.actorType, - this.actorId, - SERIALIZER, - this.clientBuilder.buildAsyncClient()); - } + /** + * Instantiates a new ActorProxy. + * + * @return New instance of ActorProxy. + */ + public ActorProxy build() { + if ((this.actorType == null) || this.actorType.isEmpty()) { + throw new IllegalArgumentException("Cannot instantiate an Actor without type."); + } + + if (this.actorId == null) { + throw new IllegalArgumentException("Cannot instantiate an Actor without Id."); + } + + // TODO: Share client between actor proxy instances. + return new ActorProxyImpl( + this.actorType, + this.actorId, + SERIALIZER, + this.clientBuilder.buildAsyncClient()); + } } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java index df3c931f3a..ae42f62398 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java @@ -12,14 +12,14 @@ */ class ActorProxyClientBuilder extends AbstractClientBuilder { - /** - * Builds an async client. - * - * @return Builds an async client. - */ - public ActorProxyAsyncClient buildAsyncClient() { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - // TODO: Expose configurations for OkHttpClient or com.microsoft.rest.RestClient. - return new ActorProxyHttpAsyncClient(super.getPort(), builder.build()); - } + /** + * Builds an async client. + * + * @return Builds an async client. + */ + public ActorProxyAsyncClient buildAsyncClient() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + // TODO: Expose configurations for OkHttpClient or com.microsoft.rest.RestClient. + return new ActorProxyHttpAsyncClient(super.getPort(), builder.build()); + } } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyHttpAsyncClient.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyHttpAsyncClient.java index a6d879f2f4..13984b8ea5 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxyHttpAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyHttpAsyncClient.java @@ -4,9 +4,9 @@ */ package io.dapr.actors.client; -import io.dapr.actors.*; import io.dapr.client.AbstractDaprHttpClient; -import okhttp3.*; +import io.dapr.utils.Constants; +import okhttp3.OkHttpClient; import reactor.core.publisher.Mono; /** @@ -14,22 +14,22 @@ */ class ActorProxyHttpAsyncClient extends AbstractDaprHttpClient implements ActorProxyAsyncClient { - /** - * Creates a new instance of {@link ActorProxyHttpAsyncClient}. - * - * @param port Port for calling Dapr. (e.g. 3500) - * @param httpClient RestClient used for all API calls in this new instance. - */ - ActorProxyHttpAsyncClient(int port, OkHttpClient httpClient) { - super(port, httpClient); - } + /** + * Creates a new instance of {@link ActorProxyHttpAsyncClient}. + * + * @param port Port for calling Dapr. (e.g. 3500) + * @param httpClient RestClient used for all API calls in this new instance. + */ + ActorProxyHttpAsyncClient(int port, OkHttpClient httpClient) { + super(port, httpClient); + } - /** - * {@inheritDoc} - */ - @Override - public Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload) { - String url = String.format(Constants.ACTOR_METHOD_RELATIVE_URL_FORMAT, actorType, actorId, methodName); - return super.invokeAPI("PUT", url, jsonPayload); - } + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload) { + String url = String.format(Constants.ACTOR_METHOD_RELATIVE_URL_FORMAT, actorType, actorId, methodName); + return super.invokeAPI("PUT", url, jsonPayload); + } } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyImpl.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyImpl.java index 44b3093f42..6e94e07964 100644 --- a/sdk/src/main/java/io/dapr/actors/client/ActorProxyImpl.java +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyImpl.java @@ -1,180 +1,144 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.utils.ObjectSerializer; +import io.dapr.actors.runtime.ActorStateSerializer; import reactor.core.publisher.Mono; import java.io.IOException; -import java.nio.charset.StandardCharsets; /** * Implements a proxy client for an Actor's instance. */ class ActorProxyImpl implements ActorProxy { - /** - * EMPTY data for null response. - */ - private static final byte[] EMPTY_DATA = new byte[0]; - - /** - * Actor's identifier for this Actor instance. - */ - private final ActorId actorId; - - /** - * Actor's type for this Actor instance. - */ - private final String actorType; - - /** - * Serializer/deserialzier to exchange message for Actors. - */ - private final ObjectSerializer serializer; - - /** - * Client to talk to the Dapr's API. - */ - private final ActorProxyAsyncClient daprClient; - - /** - * Creates a new instance of {@link ActorProxyAsyncClient}. - * - * @param actorType actor implementation type of the actor associated with the proxy object. - * @param actorId The actorId associated with the proxy - * @param serializer Serializer and deserializer for method calls. - * @param daprClient Dapr client. - */ - ActorProxyImpl(String actorType, ActorId actorId, ObjectSerializer serializer, ActorProxyAsyncClient daprClient) { - this.actorType = actorType; - this.actorId = actorId; - this.daprClient = daprClient; - this.serializer = serializer; - } - - /** - * {@inheritDoc} - */ - public ActorId getActorId() { - return actorId; - } - - /** - * {@inheritDoc} - */ - public String getActorType() { - return actorType; - } - - /** - * {@inheritDoc} - */ - @Override - public Mono invokeActorMethod(String methodName, Object data, Class clazz) { - try { - Mono result = this.daprClient.invokeActorMethod( - actorType, - actorId.toString(), - methodName, - this.wrap(data)); - - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> unwrap(s, clazz)); - } catch (IOException e) { - return Mono.error(e); + /** + * Actor's identifier for this Actor instance. + */ + private final ActorId actorId; + + /** + * Actor's type for this Actor instance. + */ + private final String actorType; + + /** + * Serializer/deserialzier to exchange message for Actors. + */ + private final ActorStateSerializer serializer; + + /** + * Client to talk to the Dapr's API. + */ + private final ActorProxyAsyncClient daprClient; + + /** + * Creates a new instance of {@link ActorProxyAsyncClient}. + * + * @param actorType actor implementation type of the actor associated with the proxy object. + * @param actorId The actorId associated with the proxy + * @param serializer Serializer and deserializer for method calls. + * @param daprClient Dapr client. + */ + ActorProxyImpl(String actorType, ActorId actorId, ActorStateSerializer serializer, ActorProxyAsyncClient daprClient) { + this.actorType = actorType; + this.actorId = actorId; + this.daprClient = daprClient; + this.serializer = serializer; } - } - - /** - * {@inheritDoc} - */ - @Override - public Mono invokeActorMethod(String methodName, Class clazz) { - Mono result = this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null); - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> unwrap(s, clazz)); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono invokeActorMethod(String methodName) { - Mono result = this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null); - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> this.unwrap(s, String.class)); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono invokeActorMethod(String methodName, Object data) { - try { - Mono result = this.daprClient.invokeActorMethod( - actorType, - actorId.toString(), - methodName, - this.wrap(data)); - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> unwrap(s, String.class)); - } catch (IOException e) { - return Mono.error(e); + + /** + * {@inheritDoc} + */ + public ActorId getActorId() { + return actorId; + } + + /** + * {@inheritDoc} + */ + public String getActorType() { + return actorType; + } + + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String methodName, Object data, Class clazz) { + try { + Mono result = this.daprClient.invokeActorMethod( + actorType, + actorId.toString(), + methodName, + this.wrap(data)); + + return result + .filter(s -> (s != null) && (!s.isEmpty())) + .map(s -> unwrap(s, clazz)); + } catch (IOException e) { + return Mono.error(e); + } } - } - - /** - * Extracts the response object from the Actor's method result. - * - * @param response String returned by API. - * @param clazz Expected response class. - * @param Expected response type. - * @return Response object, null or RuntimeException. - */ - private T unwrap(final String response, Class clazz) { - if (response == null) { - return null; + + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String methodName, Class clazz) { + Mono result = this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null); + return result + .filter(s -> (s != null) && (!s.isEmpty())) + .map(s -> unwrap(s, clazz)); } - try { - ActorMethodEnvelope res = serializer.deserialize(response, ActorMethodEnvelope.class); - if (res == null) { - return null; - } - - byte[] data = res.getData(); - if (data == null) { - return null; - } - - return this.serializer.deserialize(new String(data, StandardCharsets.UTF_8), clazz); - } catch (IOException e) { - // Wrap it to make Mono happy. - throw new RuntimeException(e); + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String methodName) { + Mono result = this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null); + return result.then(); } - } - - /** - * Builds the request to invoke an API for Actors. - * - * @param request Request object for the original Actor's method. - * @param Type for the original Actor's method request. - * @return String to be sent to Dapr's API. - * @throws IOException In case it cannot generate String. - */ - private String wrap(final T request) throws IOException { - if (request == null) { - return null; + + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String methodName, Object data) { + try { + Mono result = this.daprClient.invokeActorMethod( + actorType, + actorId.toString(), + methodName, + this.wrap(data)); + return result.then(); + } catch (IOException e) { + return Mono.error(e); + } } - String json = this.serializer.serialize(request); - ActorMethodEnvelope req = new ActorMethodEnvelope(); - req.setData(json == null ? EMPTY_DATA : json.getBytes()); - return serializer.serialize(req); - } + /** + * Extracts the response object from the Actor's method result. + * + * @param response String returned by API. + * @param clazz Expected response class. + * @param Expected response type. + * @return Response object, null or RuntimeException. + */ + private T unwrap(final String response, Class clazz) { + return this.serializer.unwrapMethodResponse(response, clazz); + } + + /** + * Builds the request to invoke an API for Actors. + * + * @param request Request object for the original Actor's method. + * @param Type for the original Actor's method request. + * @return String to be sent to Dapr's API. + * @throws IOException In case it cannot generate String. + */ + private String wrap(final T request) throws IOException { + return this.serializer.wrapMethodRequest(request); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java b/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java index c1462f029a..042dc1656d 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java @@ -16,257 +16,293 @@ /** * Represents the base class for actors. - * + *

* The base type for actors, that provides the common functionality * for actors that derive from {@link Actor}. * The state is preserved across actor garbage collections and fail-overs. */ public abstract class AbstractActor { - /** - * Type of tracing messages. - */ - private static final String TRACE_TYPE = "Actor"; - - /** - * Context for the Actor runtime. - */ - private final ActorRuntimeContext actorRuntimeContext; - - /** - * Actor identifier. - */ - private final ActorId id; - - /** - * Manager for the states in Actors. - */ - private final ActorStateManager actorStateManager; - - /** - * Emits trace messages for Actors. - */ - private final ActorTrace actorTrace; - - /** - * Registered timers for this Actor. - */ - private final Map> timers; - - /** - * Instantiates a new Actor. - * @param runtimeContext Context for the runtime. - * @param id Actor identifier. - */ - protected AbstractActor(ActorRuntimeContext runtimeContext, ActorId id) { - this.actorRuntimeContext = runtimeContext; - this.id = id; - this.actorStateManager = new ActorStateManager( - runtimeContext.getStateProvider(), - runtimeContext.getActorTypeInformation().getName(), - id); - this.actorTrace = runtimeContext.getActorTrace(); - this.timers = Collections.synchronizedMap(new HashMap<>()); - } - - /** - * Registers a reminder for this Actor. - * @param reminderName Name of the reminder. - * @param data Data to be send along with reminder triggers. - * @param dueTime Due time for the first trigger. - * @param period Frequency for the triggers. - * @return Asynchronous void response. - */ - protected Mono registerReminder( - String reminderName, - String data, - Duration dueTime, - Duration period) { - try { - ActorReminderParams params = new ActorReminderParams(data, dueTime, period); - String serialized = this.actorRuntimeContext.getActorSerializer().serialize(params); - return this.actorRuntimeContext.getDaprClient().registerReminder( - this.actorRuntimeContext.getActorTypeInformation().getName(), - this.id.toString(), - reminderName, - serialized); - } catch (IOException e) { - return Mono.error(e); + /** + * Type of tracing messages. + */ + private static final String TRACE_TYPE = "Actor"; + + /** + * Context for the Actor runtime. + */ + private final ActorRuntimeContext actorRuntimeContext; + + /** + * Actor identifier. + */ + private final ActorId id; + + /** + * Manager for the states in Actors. + */ + private final ActorStateManager actorStateManager; + + /** + * Emits trace messages for Actors. + */ + private final ActorTrace actorTrace; + + /** + * Registered timers for this Actor. + */ + private final Map timers; + + /** + * Instantiates a new Actor. + * + * @param runtimeContext Context for the runtime. + * @param id Actor identifier. + */ + protected AbstractActor(ActorRuntimeContext runtimeContext, ActorId id) { + this.actorRuntimeContext = runtimeContext; + this.id = id; + this.actorStateManager = new ActorStateManager( + runtimeContext.getStateProvider(), + runtimeContext.getActorTypeInformation().getName(), + id); + this.actorTrace = runtimeContext.getActorTrace(); + this.timers = Collections.synchronizedMap(new HashMap<>()); + } + + /** + * Returns the id of the actor. + * + * @return Actor id. + */ + protected ActorId getId() { + return this.id; + } + + /** + * Registers a reminder for this Actor. + * + * @param reminderName Name of the reminder. + * @param state State to be send along with reminder triggers. + * @param dueTime Due time for the first trigger. + * @param period Frequency for the triggers. + * @param Type of the state object. + * @return Asynchronous void response. + */ + protected Mono registerReminder( + String reminderName, + T state, + Duration dueTime, + Duration period) { + try { + String data = this.actorRuntimeContext.getActorSerializer().serialize(state); + ActorReminderParams params = new ActorReminderParams(data, dueTime, period); + String serialized = this.actorRuntimeContext.getActorSerializer().serialize(params); + return this.actorRuntimeContext.getDaprClient().registerReminder( + this.actorRuntimeContext.getActorTypeInformation().getName(), + this.id.toString(), + reminderName, + serialized); + } catch (IOException e) { + return Mono.error(e); + } + } + + /** + * Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. + * + * @param timerName Name of the timer, unique per Actor (auto-generated if null). + * @param callback Name of the method to be called. + * @param state State to be passed it to the method when timer triggers. + * @param dueTime The amount of time to delay before the async callback is first invoked. + * Specify negative one (-1) milliseconds to prevent the timer from starting. + * Specify zero (0) to start the timer immediately. + * @param period The time interval between invocations of the async callback. + * Specify negative one (-1) milliseconds to disable periodic signaling. + * @param Type for the state to be passed in to timer. + * @return Asynchronous result. + */ + protected Mono registerActorTimer( + String timerName, + String callback, + T state, + Duration dueTime, + Duration period) { + if ((callback == null) || callback.isEmpty()) { + throw new IllegalArgumentException("Timer requires a callback function."); + } + + String name = timerName; + if ((timerName == null) || (timerName.isEmpty())) { + name = String.format("%s_Timer_%d", this.id.toString(), this.timers.size() + 1); + } + + try { + ActorTimer actorTimer = new ActorTimer(this, name, callback, state, dueTime, period); + String serializedTimer = this.actorRuntimeContext.getActorSerializer().serialize(actorTimer); + + this.timers.put(name, actorTimer); + return this.actorRuntimeContext.getDaprClient().registerTimer( + this.actorRuntimeContext.getActorTypeInformation().getName(), + this.id.toString(), + name, + serializedTimer); + } catch (IOException e) { + return Mono.error(e); + } + } + + /** + * Unregisters an Actor timer. + * + * @param actorTimer Timer to be unregistered. + * @return Asynchronous void response. + */ + protected Mono unregister(ActorTimer actorTimer) { + return this.actorRuntimeContext.getDaprClient().unregisterTimer( + this.actorRuntimeContext.getActorTypeInformation().getName(), + this.id.toString(), + actorTimer.getName()) + .then(this.onUnregisteredTimer(actorTimer)); + } + + /** + * Callback function invoked after an Actor has been activated. + * + * @return Asynchronous void response. + */ + protected Mono onActivate() { + return Mono.empty(); + } + + /** + * Callback function invoked after an Actor has been deactivated. + * + * @return Asynchronous void response. + */ + protected Mono onDeactivate() { + return Mono.empty(); } - } - - /** - * Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it. - * - * @param timerName Name of the timer, unique per Actor (auto-generated if null). - * @param methodName Name of the method to be called. - * @param state State object to be passed it to the method when timer triggers. - * @param dueTime The amount of time to delay before the async callback is first invoked. - * Specify negative one (-1) milliseconds to prevent the timer from starting. - * Specify zero (0) to start the timer immediately. - * @param period The time interval between invocations of the async callback. - * Specify negative one (-1) milliseconds to disable periodic signaling. - * @param Type for the state object. - * @return Asynchronous result. - */ - protected Mono registerActorTimer( - String timerName, - String methodName, - S state, - Duration dueTime, - Duration period) { - String name = timerName; - if ((timerName == null) || (timerName.isEmpty())) { - name = String.format("%s_Timer_%d", this.id.toString(), this.timers.size() + 1); + + /** + * Callback function invoked before method is invoked. + * + * @param actorMethodContext Method context. + * @return Asynchronous void response. + */ + protected Mono onPreActorMethod(ActorMethodContext actorMethodContext) { + return Mono.empty(); + } + + /** + * Callback function invoked after method is invoked. + * + * @param actorMethodContext Method context. + * @return Asynchronous void response. + */ + protected Mono onPostActorMethod(ActorMethodContext actorMethodContext) { + return Mono.empty(); + } + + /** + * Saves the state of this Actor. + * + * @return Asynchronous void response. + */ + protected Mono saveState() { + return this.actorStateManager.save(); + } + + /** + * Resets the state of this Actor. + * + * @return Asynchronous void response. + */ + Mono resetState() { + return this.actorStateManager.clear(); + } + + /** + * Gets a given timer by name. + * + * @param timerName Timer name. + * @return Asynchronous void response. + */ + ActorTimer getActorTimer(String timerName) { + return timers.getOrDefault(timerName, null); + } + + /** + * Internal callback when an Actor is activated. + * + * @return Asynchronous void response. + */ + Mono onActivateInternal() { + this.actorTrace.writeInfo(TRACE_TYPE, this.id.toString(), "Activating ..."); + + return this.resetState() + .then(this.onActivate()) + .then(this.doWriteInfo(TRACE_TYPE, this.id.toString(), "Activated")) + .then(this.saveState()); + } + + /** + * Internal callback when an Actor is deactivated. + * + * @return Asynchronous void response. + */ + Mono onDeactivateInternal() { + this.actorTrace.writeInfo(TRACE_TYPE, this.id.toString(), "Deactivating ..."); + + return this.resetState() + .then(this.onDeactivate()) + .then(this.doWriteInfo(TRACE_TYPE, this.id.toString(), "Deactivated")) + .then(this.saveState()); + } + + /** + * Internal callback prior to method be invoked. + * + * @param actorMethodContext Method context. + * @return Asynchronous void response. + */ + Mono onPreActorMethodInternal(ActorMethodContext actorMethodContext) { + return this.onPreActorMethod(actorMethodContext); + } + + /** + * Internal callback after method is invoked. + * + * @param actorMethodContext Method context. + * @return Asynchronous void response. + */ + Mono onPostActorMethodInternal(ActorMethodContext actorMethodContext) { + return this.onPostActorMethod(actorMethodContext) + .then(this.saveState()); + } + + /** + * Internal callback for when Actor timer is unregistered. + * + * @param timer Timer being unregistered. + * @return Asynchronous void response. + */ + Mono onUnregisteredTimer(ActorTimer timer) { + this.timers.remove(timer.getName()); + return Mono.empty(); } - ActorTimer actorTimer = new ActorTimer(this, name, methodName, state, dueTime, period); - String serializedTimer = null; - try { - serializedTimer = this.actorRuntimeContext.getActorSerializer().serialize(actorTimer); - } catch (IOException e) { - return Mono.error(e); + /** + * Internal method to emit a trace message. + * + * @param type Type of trace message. + * @param id Identifier of entity relevant for the trace message. + * @param message Message to be logged. + * @return Asynchronous void response. + */ + private Mono doWriteInfo(String type, String id, String message) { + this.actorTrace.writeInfo(type, id, message); + return Mono.empty(); } - this.timers.put(name, actorTimer); - return this.actorRuntimeContext.getDaprClient().registerTimer( - this.actorRuntimeContext.getActorTypeInformation().getName(), - this.id.toString(), - name, - serializedTimer); - } - - /** - * Unregisters an Actor timer. - * @param actorTimer Timer to be unregistered. - * @return Asynchronous void response. - */ - protected Mono unregister(ActorTimer actorTimer) { - return this.actorRuntimeContext.getDaprClient().unregisterTimer( - this.actorRuntimeContext.getActorTypeInformation().getName(), - this.id.toString(), - actorTimer.getName()) - .then(this.onUnregisteredTimer(actorTimer)); - } - - /** - * Callback function invoked after an Actor has been activated. - * @return Asynchronous void response. - */ - protected Mono onActivate() { return Mono.empty(); } - - /** - * Callback function invoked after an Actor has been deactivated. - * @return Asynchronous void response. - */ - protected Mono onDeactivate() { return Mono.empty(); } - - /** - * Callback function invoked before method is invoked. - * @param actorMethodContext Method context. - * @return Asynchronous void response. - */ - protected Mono onPreActorMethod(ActorMethodContext actorMethodContext) { - return Mono.empty(); - } - - /** - * Callback function invoked after method is invoked. - * @param actorMethodContext Method context. - * @return Asynchronous void response. - */ - protected Mono onPostActorMethod(ActorMethodContext actorMethodContext) { - return Mono.empty(); - } - - /** - * Saves the state of this Actor. - * @return Asynchronous void response. - */ - protected Mono saveState() { - return this.actorStateManager.save(); - } - - /** - * Resets the state of this Actor. - * @return Asynchronous void response. - */ - Mono resetState() { return this.actorStateManager.clear(); } - - /** - * Gets a given timer by name. - * @param timerName Timer name. - * @return Asynchronous void response. - */ - ActorTimer getActorTimer(String timerName) - { - return timers.getOrDefault(timerName, null); - } - - /** - * Internal callback when an Actor is activated. - * @return Asynchronous void response. - */ - Mono onActivateInternal() { - this.actorTrace.writeInfo(TRACE_TYPE, this.id.toString(), "Activating ..."); - - return this.resetState() - .then(this.onActivate()) - .then(this.doWriteInfo(TRACE_TYPE, this.id.toString(), "Activated")) - .then(this.saveState()); - } - - /** - * Internal callback when an Actor is deactivated. - * @return Asynchronous void response. - */ - Mono onDeactivateInternal() { - this.actorTrace.writeInfo(TRACE_TYPE, this.id.toString(), "Deactivating ..."); - - return this.resetState() - .then(this.onDeactivate()) - .then(this.doWriteInfo(TRACE_TYPE, this.id.toString(), "Deactivated")) - .then(this.saveState()); - } - - /** - * Internal callback prior to method be invoked. - * @param actorMethodContext Method context. - * @return Asynchronous void response. - */ - Mono onPreActorMethodInternal(ActorMethodContext actorMethodContext) { - return this.onPreActorMethod(actorMethodContext); - } - - /** - * Internal callback after method is invoked. - * @param actorMethodContext Method context. - * @return Asynchronous void response. - */ - Mono onPostActorMethodInternal(ActorMethodContext actorMethodContext) { - return this.onPostActorMethod(actorMethodContext) - .then(this.saveState()); - } - - /** - * Internal callback for when Actor timer is unregistered. - * @param timer Timer being unregistered. - * @return Asynchronous void response. - */ - Mono onUnregisteredTimer(ActorTimer timer) { - this.timers.remove(timer.getName()); - return Mono.empty(); - } - - /** - * Internal method to emit a trace message. - * @param type Type of trace message. - * @param id Identifier of entity relevant for the trace message. - * @param message Message to be logged. - * @return Asynchronous void response. - */ - private Mono doWriteInfo(String type, String id, String message) { - this.actorTrace.writeInfo(type, id, message); - return Mono.empty(); - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorCallType.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorCallType.java index a57ebea7a0..063a4cab1c 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorCallType.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorCallType.java @@ -9,17 +9,17 @@ */ enum ActorCallType { - /** - * Specifies that the method invoked is an actor interface method for a given - * client request. - */ - ACTOR_INTERFACE_METHOD, - /** - * Specifies that the method invoked is a timer callback method. - */ - TIMER_METHOD, - /** - * Specifies that the method is when a reminder fires. - */ - REMINDER_METHOD + /** + * Specifies that the method invoked is an actor interface method for a given + * client request. + */ + ACTOR_INTERFACE_METHOD, + /** + * Specifies that the method invoked is a timer callback method. + */ + TIMER_METHOD, + /** + * Specifies that the method is when a reminder fires. + */ + REMINDER_METHOD } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorFactory.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorFactory.java index 18c386d2ac..1e56d7f456 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorFactory.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorFactory.java @@ -9,16 +9,18 @@ /** * Creates an actor of a given type. + * * @param Actor Type to be created. */ @FunctionalInterface public interface ActorFactory { - /** - * Creates an Actor. - * @param actorRuntimeContext Actor type's context in the runtime. - * @param actorId Actor Id. - * @return Actor or null it failed. - */ - T createActor(ActorRuntimeContext actorRuntimeContext, ActorId actorId); + /** + * Creates an Actor. + * + * @param actorRuntimeContext Actor type's context in the runtime. + * @param actorId Actor Id. + * @return Actor or null it failed. + */ + T createActor(ActorRuntimeContext actorRuntimeContext, ActorId actorId); } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java index 90cfa2a4be..e07fcb6b05 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java @@ -15,246 +15,256 @@ */ class ActorManager { - /** - * Context for the Actor runtime. - */ - private final ActorRuntimeContext runtimeContext; + /** + * Context for the Actor runtime. + */ + private final ActorRuntimeContext runtimeContext; - /** - * Methods found in Actors. - */ - private final ActorMethodInfoMap actorMethods; + /** + * Methods found in Actors. + */ + private final ActorMethodInfoMap actorMethods; - /** - * Active Actor instances. - */ - private final Map activeActors; + /** + * Active Actor instances. + */ + private final Map activeActors; - /** - * Instantiates a new manager for a given actor referenced in the runtimeContext. - * @param runtimeContext Runtime context for the Actor. - */ - ActorManager(ActorRuntimeContext runtimeContext) { - this.runtimeContext = runtimeContext; - this.actorMethods = new ActorMethodInfoMap(runtimeContext.getActorTypeInformation().getInterfaces()); - this.activeActors = Collections.synchronizedMap(new HashMap<>()); - } - - /** - * Activates an Actor. - * @param actorId Actor identifier. - * @return Asynchronous void response. - */ - Mono activateActor(ActorId actorId) { - T actor = this.runtimeContext.getActorFactory().createActor(runtimeContext, actorId); + /** + * Instantiates a new manager for a given actor referenced in the runtimeContext. + * + * @param runtimeContext Runtime context for the Actor. + */ + ActorManager(ActorRuntimeContext runtimeContext) { + this.runtimeContext = runtimeContext; + this.actorMethods = new ActorMethodInfoMap(runtimeContext.getActorTypeInformation().getInterfaces()); + this.activeActors = Collections.synchronizedMap(new HashMap<>()); + } - return actor.onActivateInternal().then(this.onActivatedActor(actorId, actor)); - } + /** + * Activates an Actor. + * + * @param actorId Actor identifier. + * @return Asynchronous void response. + */ + Mono activateActor(ActorId actorId) { + T actor = this.runtimeContext.getActorFactory().createActor(runtimeContext, actorId); - /** - * Deactivates an Actor. - * @param actorId Actor identifier. - * @return Asynchronous void response. - */ - Mono deactivateActor(ActorId actorId) { - T actor = this.activeActors.remove(actorId); - if (actor != null) { - return actor.onDeactivateInternal(); + return actor.onActivateInternal().then(this.onActivatedActor(actorId, actor)); } - return Mono.empty(); - } + /** + * Deactivates an Actor. + * + * @param actorId Actor identifier. + * @return Asynchronous void response. + */ + Mono deactivateActor(ActorId actorId) { + T actor = this.activeActors.remove(actorId); + if (actor != null) { + return actor.onDeactivateInternal(); + } - /** - * Invokes a given method in the Actor. - * @param actorId Identifier for Actor being invoked. - * @param methodName Name of method being invoked. - * @param request Input object for the method being invoked. - * @return Asynchronous void response. - */ - Mono invokeMethod(ActorId actorId, String methodName, String request) { - return invokeMethod(actorId, null, methodName, request); - } + return Mono.empty(); + } - /** - * Invokes reminder for Actor. - * @param actorId Identifier for Actor being invoked. - * @param reminderName Name of reminder being invoked. - * @param request Input object for the reminder being invoked. - * @return Asynchronous void response. - */ - Mono invokeReminder(ActorId actorId, String reminderName, String request) { - if (!this.runtimeContext.getActorTypeInformation().isRemindable()) { - return Mono.empty(); + /** + * Invokes a given method in the Actor. + * + * @param actorId Identifier for Actor being invoked. + * @param methodName Name of method being invoked. + * @param request Input object for the method being invoked. + * @return Asynchronous void response. + */ + Mono invokeMethod(ActorId actorId, String methodName, String request) { + return invokeMethod(actorId, null, methodName, request); } - try { - ActorReminderParams reminder = this.runtimeContext.getActorSerializer().deserialize(request, ActorReminderParams.class); + /** + * Invokes reminder for Actor. + * + * @param actorId Identifier for Actor being invoked. + * @param reminderName Name of reminder being invoked. + * @param request Parameters for the reminder. + * @return Asynchronous void response. + */ + Mono invokeReminder(ActorId actorId, String reminderName, String request) { + if (!this.runtimeContext.getActorTypeInformation().isRemindable()) { + return Mono.empty(); + } + + try { + ActorReminderParams params = this.runtimeContext.getActorSerializer().deserialize(request, ActorReminderParams.class); - return invoke( - actorId, - ActorMethodContext.CreateForReminder(reminderName), - actor -> doReminderInvokation((Remindable)actor, reminderName, reminder)) - .then(); - } catch (Exception e) { - return Mono.error(e); + return invoke( + actorId, + ActorMethodContext.CreateForReminder(reminderName), + actor -> doReminderInvokation((Remindable) actor, reminderName, params)) + .then(); + } catch (Exception e) { + return Mono.error(e); + } } - } - /** - * Invokes a timer for a given Actor. - * @param actorId Identifier for Actor. - * @param timerName Name of timer being invoked. - * @return Asynchronous void response. - */ - Mono invokeTimer(ActorId actorId, String timerName) { - try { - AbstractActor actor = this.activeActors.getOrDefault(actorId, null); - if (actor == null) { - throw new IllegalArgumentException( - String.format("Could not find actor %s of type %s.", - actorId.toString(), - this.runtimeContext.getActorTypeInformation().getName())); - } + /** + * Invokes a timer for a given Actor. + * + * @param actorId Identifier for Actor. + * @param timerName Name of timer being invoked. + * @return Asynchronous void response. + */ + Mono invokeTimer(ActorId actorId, String timerName) { + try { + AbstractActor actor = this.activeActors.getOrDefault(actorId, null); + if (actor == null) { + throw new IllegalArgumentException( + String.format("Could not find actor %s of type %s.", + actorId.toString(), + this.runtimeContext.getActorTypeInformation().getName())); + } - ActorTimer actorTimer = actor.getActorTimer(timerName); - if (actorTimer == null) { - throw new IllegalStateException( - String.format("Could not find timer %s for actor %s.", - timerName, - this.runtimeContext.getActorTypeInformation().getName())); - } + ActorTimer actorTimer = actor.getActorTimer(timerName); + if (actorTimer == null) { + throw new IllegalStateException( + String.format("Could not find timer %s for actor %s.", + timerName, + this.runtimeContext.getActorTypeInformation().getName())); + } - return invokeMethod( - actorId, - ActorMethodContext.CreateForTimer(timerName), - actorTimer.getMethodName(), - actorTimer.getState()) - .then(); - } catch (Exception e) { - return Mono.error(e); + return invokeMethod( + actorId, + ActorMethodContext.CreateForTimer(timerName), + actorTimer.getCallback(), + actorTimer.getState()) + .then(); + } catch (Exception e) { + return Mono.error(e); + } } - } - - /** - * Internal callback for when Actor is activated. - * @param actorId Actor identifier. - * @param actor Actor's instance. - * @return Asynchronous void response. - */ - private Mono onActivatedActor(ActorId actorId, T actor) { - this.activeActors.put(actorId, actor); - return Mono.empty(); - } - /** - * Internal method to actually invoke a reminder. - * @param actor Actor that owns the reminder. - * @param reminderName Name of the reminder. - * @param reminderParams Params for the reminder. - * @return Asynchronous void response. - */ - private Mono doReminderInvokation( - Remindable actor, - String reminderName, - ActorReminderParams reminderParams) { - try { - Object data = this.runtimeContext.getActorSerializer().deserialize( - reminderParams.getData(), - actor.getReminderStateType()); - return actor.receiveReminder( - reminderName, - data, - reminderParams.getDueTime(), - reminderParams.getPeriod()); - } catch (IOException e) { - return Mono.error(e); + /** + * Internal callback for when Actor is activated. + * + * @param actorId Actor identifier. + * @param actor Actor's instance. + * @return Asynchronous void response. + */ + private Mono onActivatedActor(ActorId actorId, T actor) { + this.activeActors.put(actorId, actor); + return Mono.empty(); } - } - /** - * Internal method to actually invoke Actor's method. - * @param actorId Identifier for the Actor. - * @param context Method context to be invoked. - * @param methodName Method name to be invoked. - * @param request Input object to be passed in to the invoked method. - * @return Asynchronous void response. - */ - private Mono invokeMethod(ActorId actorId, ActorMethodContext context, String methodName, Object request) { - ActorMethodContext actorMethodContext = context; - if (actorMethodContext == null) { - actorMethodContext = ActorMethodContext.CreateForActor(methodName); + /** + * Internal method to actually invoke a reminder. + * + * @param actor Actor that owns the reminder. + * @param reminderName Name of the reminder. + * @param reminderParams Params for the reminder. + * @return Asynchronous void response. + */ + private Mono doReminderInvokation( + Remindable actor, + String reminderName, + ActorReminderParams reminderParams) { + try { + Object data = this.runtimeContext.getActorSerializer().deserialize( + reminderParams.getData(), + actor.getStateType()); + return actor.receiveReminder( + reminderName, + data, + reminderParams.getDueTime(), + reminderParams.getPeriod()); + } catch (IOException e) { + return Mono.error(e); + } } - return this.invoke(actorId, actorMethodContext, actor -> { - try { - // Finds the actor method with the given name and 1 or no parameter. - Method method = this.actorMethods.get(methodName); + /** + * Internal method to actually invoke Actor's method. + * + * @param actorId Identifier for the Actor. + * @param context Method context to be invoked. + * @param methodName Method name to be invoked. + * @param request Input object to be passed in to the invoked method. + * @return Asynchronous void response. + */ + private Mono invokeMethod(ActorId actorId, ActorMethodContext context, String methodName, Object request) { + ActorMethodContext actorMethodContext = context; + if (actorMethodContext == null) { + actorMethodContext = ActorMethodContext.CreateForActor(methodName); + } - Object response; + return this.invoke(actorId, actorMethodContext, actor -> { + try { + // Finds the actor method with the given name and 1 or no parameter. + Method method = this.actorMethods.get(methodName); - if (method.getParameterCount() == 0) { - response = method.invoke(actor); - } else { - // Actor methods must have a one or no parameter, which is guaranteed at this point. - Class inputClass = method.getParameterTypes()[0]; + Object response; - if ((request != null) && !inputClass.isInstance(request)) { - // If request object is String, we deserialize it. - response = method.invoke( - actor, - this.runtimeContext.getActorSerializer().deserialize((String) request, inputClass)); - } else { - // If input already of the right type, so we just cast it. - response = method.invoke(actor, inputClass.cast(request)); - } - } + if (method.getParameterCount() == 0) { + response = method.invoke(actor); + } else { + // Actor methods must have a one or no parameter, which is guaranteed at this point. + Class inputClass = method.getParameterTypes()[0]; - if (response == null) { - return Mono.empty(); - } + if ((request != null) && !inputClass.isInstance(request)) { + // If request object is String, we deserialize it. + response = method.invoke( + actor, + this.runtimeContext.getActorSerializer().deserialize(request, inputClass)); + } else { + // If input already of the right type, so we just cast it. + response = method.invoke(actor, inputClass.cast(request)); + } + } - if (response instanceof Mono) { - return ((Mono) response).map(r -> { - try { - return this.runtimeContext.getActorSerializer().serialize(r); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } + if (response == null) { + return Mono.empty(); + } + + if (response instanceof Mono) { + return ((Mono) response).map(r -> { + try { + return this.runtimeContext.getActorSerializer().serialize(r); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } - // Method was not Mono, so we serialize response. - return Mono.just(this.runtimeContext.getActorSerializer().serialize(response)); - } catch (Exception e) { - return Mono.error(e); - } - }).map(r -> r.toString()); - } + // Method was not Mono, so we serialize response. + return Mono.just(this.runtimeContext.getActorSerializer().serialize(response)); + } catch (Exception e) { + return Mono.error(e); + } + }).map(r -> r.toString()); + } - /** - * Internal call to invoke a method, timer or reminder for an Actor. - * @param actorId Actor identifier. - * @param context Context for the method/timer/reminder call. - * @param func Function to perform the method call. - * @param Expected return type for the function call. - * @return Asynchronous response for the returned object. - */ - private Mono invoke(ActorId actorId, ActorMethodContext context, Function> func) { - try { - AbstractActor actor = this.activeActors.getOrDefault(actorId, null); - if (actor == null) { - throw new IllegalArgumentException( - String.format("Could not find actor %s of type %s.", - actorId.toString(), - this.runtimeContext.getActorTypeInformation().getName())); - } + /** + * Internal call to invoke a method, timer or reminder for an Actor. + * + * @param actorId Actor identifier. + * @param context Context for the method/timer/reminder call. + * @param func Function to perform the method call. + * @param Expected return type for the function call. + * @return Asynchronous response for the returned object. + */ + private Mono invoke(ActorId actorId, ActorMethodContext context, Function> func) { + try { + AbstractActor actor = this.activeActors.getOrDefault(actorId, null); + if (actor == null) { + throw new IllegalArgumentException( + String.format("Could not find actor %s of type %s.", + actorId.toString(), + this.runtimeContext.getActorTypeInformation().getName())); + } - return actor.onPreActorMethodInternal(context).then( - func.apply(actor).flatMap(result -> actor.onPostActorMethodInternal(context).thenReturn(result)) - ); - } catch (Exception e) { - return Mono.error(e); + return actor.onPreActorMethodInternal(context).then( + func.apply(actor).flatMap(result -> actor.onPostActorMethodInternal(context).thenReturn(result)) + ); + } catch (Exception e) { + return Mono.error(e); + } } - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodContext.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodContext.java index 6826395efc..f287928aa3 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodContext.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodContext.java @@ -9,72 +9,72 @@ */ public class ActorMethodContext { - /** - * Method name to be invoked. - */ - private final String methodName; + /** + * Method name to be invoked. + */ + private final String methodName; - /** - * Call type to be used. - */ - private final ActorCallType callType; + /** + * Call type to be used. + */ + private final ActorCallType callType; - /** - * Constructs a new instance of {@link ActorMethodContext}, representing a call for an Actor. - * - * @param methodName Method name to be invoked. - * @param callType Call type to be used. - */ - private ActorMethodContext(String methodName, ActorCallType callType) { - this.methodName = methodName; - this.callType = callType; - } + /** + * Constructs a new instance of {@link ActorMethodContext}, representing a call for an Actor. + * + * @param methodName Method name to be invoked. + * @param callType Call type to be used. + */ + private ActorMethodContext(String methodName, ActorCallType callType) { + this.methodName = methodName; + this.callType = callType; + } - /** - * Gets the name of the method invoked by actor runtime. - * - * @return The method name. - */ - public String getMethodName() { - return this.methodName; - } + /** + * Gets the name of the method invoked by actor runtime. + * + * @return The method name. + */ + public String getMethodName() { + return this.methodName; + } - /** - * Gets the call type to be used. - * - * @return Call type. - */ - public ActorCallType getCallType() { - return this.callType; - } + /** + * Gets the call type to be used. + * + * @return Call type. + */ + public ActorCallType getCallType() { + return this.callType; + } - /** - * Creates a context to invoke an Actor's method. - * - * @param methodName THe method to be invoked. - * @return Context of the method call as {@link ActorMethodContext} - */ - static ActorMethodContext CreateForActor(String methodName) { - return new ActorMethodContext(methodName, ActorCallType.ACTOR_INTERFACE_METHOD); - } + /** + * Creates a context to invoke an Actor's method. + * + * @param methodName THe method to be invoked. + * @return Context of the method call as {@link ActorMethodContext} + */ + static ActorMethodContext CreateForActor(String methodName) { + return new ActorMethodContext(methodName, ActorCallType.ACTOR_INTERFACE_METHOD); + } - /** - * Creates a context to invoke an Actor's timer. - * - * @param methodName THe method to be invoked. - * @return Context of the method call as {@link ActorMethodContext} - */ - static ActorMethodContext CreateForTimer(String methodName) { - return new ActorMethodContext(methodName, ActorCallType.TIMER_METHOD); - } + /** + * Creates a context to invoke an Actor's timer. + * + * @param methodName THe method to be invoked. + * @return Context of the method call as {@link ActorMethodContext} + */ + static ActorMethodContext CreateForTimer(String methodName) { + return new ActorMethodContext(methodName, ActorCallType.TIMER_METHOD); + } - /** - * Creates a context to invoke an Actor's reminder. - * - * @param methodName THe method to be invoked. - * @return Context of the method call as {@link ActorMethodContext} - */ - static ActorMethodContext CreateForReminder(String methodName) { - return new ActorMethodContext(methodName, ActorCallType.REMINDER_METHOD); - } + /** + * Creates a context to invoke an Actor's reminder. + * + * @param methodName THe method to be invoked. + * @return Context of the method call as {@link ActorMethodContext} + */ + static ActorMethodContext CreateForReminder(String methodName) { + return new ActorMethodContext(methodName, ActorCallType.REMINDER_METHOD); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodInfoMap.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodInfoMap.java index 453518b1a1..c9dfd3e636 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodInfoMap.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorMethodInfoMap.java @@ -10,45 +10,47 @@ * Actor method dispatcher map. Holds method_name -> Method for methods defined in Actor interfaces. */ class ActorMethodInfoMap { - /** - * Map for methods based on name. - */ - private final Map methods; + /** + * Map for methods based on name. + */ + private final Map methods; - /** - * Instantiates a given Actor map based on the interfaces found in the class. - * @param interfaceTypes Interfaces found in the Actor class. - */ - ActorMethodInfoMap(Collection> interfaceTypes) { - Map methods = new HashMap<>(); + /** + * Instantiates a given Actor map based on the interfaces found in the class. + * + * @param interfaceTypes Interfaces found in the Actor class. + */ + ActorMethodInfoMap(Collection> interfaceTypes) { + Map methods = new HashMap<>(); - // Find methods which are defined in Actor interface. - for (Class actorInterface : interfaceTypes) { - for (Method methodInfo : actorInterface.getMethods()) { - // Only support methods with 1 or 0 argument. - if (methodInfo.getParameterCount() <= 1) { - // If Actor class uses overloading, then one will win. - // Document this behavior, so users know how to write their code. - methods.put(methodInfo.getName(), methodInfo); + // Find methods which are defined in Actor interface. + for (Class actorInterface : interfaceTypes) { + for (Method methodInfo : actorInterface.getMethods()) { + // Only support methods with 1 or 0 argument. + if (methodInfo.getParameterCount() <= 1) { + // If Actor class uses overloading, then one will win. + // Document this behavior, so users know how to write their code. + methods.put(methodInfo.getName(), methodInfo); + } + } } - } + + this.methods = Collections.unmodifiableMap(methods); } - this.methods = Collections.unmodifiableMap(methods); - } + /** + * Gets the Actor's method by name. + * + * @param methodName Name of the method. + * @return Method. + * @throws NoSuchMethodException If method is not found. + */ + Method get(String methodName) throws NoSuchMethodException { + Method method = this.methods.get(methodName); + if (method == null) { + throw new NoSuchMethodException(String.format("Could not find method %s.", methodName)); + } - /** - * Gets the Actor's method by name. - * @param methodName Name of the method. - * @return Method. - * @throws NoSuchMethodException If method is not found. - */ - Method get(String methodName) throws NoSuchMethodException { - Method method = this.methods.get(methodName); - if (method == null) { - throw new NoSuchMethodException(String.format("Could not find method %s.", methodName)); + return method; } - - return method; - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java index 2ac3e030a6..96b7404021 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java @@ -34,9 +34,10 @@ final class ActorReminderParams { /** * Instantiates a new instance for the params of a reminder. - * @param data Data to be passed in as part of the reminder trigger. + * + * @param data Data to be passed in as part of the reminder trigger. * @param dueTime Time the reminder is due for the 1st time. - * @param period Interval between triggers. + * @param period Interval between triggers. */ ActorReminderParams(String data, Duration dueTime, Duration period) { ValidateDueTime("DueTime", dueTime); @@ -48,6 +49,7 @@ final class ActorReminderParams { /** * Gets the time the reminder is due for the 1st time. + * * @return Time the reminder is due for the 1st time. */ Duration getDueTime() { @@ -56,6 +58,7 @@ Duration getDueTime() { /** * Gets the interval between triggers. + * * @return Interval between triggers. */ Duration getPeriod() { @@ -64,6 +67,7 @@ Duration getPeriod() { /** * Gets the data to be passed in as part of the reminder trigger. + * * @return Data to be passed in as part of the reminder trigger. */ String getData() { @@ -72,8 +76,9 @@ String getData() { /** * Validates due time is valid, throws {@link IllegalArgumentException}. + * * @param argName Name of the argument passed in. - * @param value Vale being checked. + * @param value Vale being checked. */ private static void ValidateDueTime(String argName, Duration value) { if (value.compareTo(Duration.ZERO) < 0) { @@ -85,8 +90,9 @@ private static void ValidateDueTime(String argName, Duration value) { /** * Validates reminder period is valid, throws {@link IllegalArgumentException}. + * * @param argName Name of the argument passed in. - * @param value Vale being checked. + * @param value Vale being checked. */ private static void ValidatePeriod(String argName, Duration value) throws IllegalArgumentException { if (value.compareTo(MIN_TIME_PERIOD) < 0) { diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java index 4436e41226..a82605f861 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java @@ -19,201 +19,203 @@ */ public class ActorRuntime { - /** - * A trace type used when logging. - */ - private static final String TRACE_TYPE = "ActorRuntime"; - - /** - * Tracing errors, warnings and info logs. - */ - private static final ActorTrace ACTOR_TRACE = new ActorTrace(); - - /** - * Gets an instance to the ActorRuntime. There is only 1. - */ - private static volatile ActorRuntime instance; - - /** - * A client used to communicate from the actor to the Dapr runtime. - */ - private final AppToDaprAsyncClient appToDaprAsyncClient; - - /** - * State provider for Dapr. - */ - private final DaprStateAsyncProvider daprStateProvider; - - /** - * Serializes/deserializes objects for Actors. - */ - private final ActorStateSerializer actorSerializer; - - /** - * Map of ActorType --> ActorManager. - */ - private final Map actorManagers; - - /** - * The default constructor. This should not be called directly. - * - * @throws IllegalStateException - */ - private ActorRuntime() throws IllegalStateException { - if (instance != null) { - throw new IllegalStateException("ActorRuntime should only be constructed once"); + /** + * A trace type used when logging. + */ + private static final String TRACE_TYPE = "ActorRuntime"; + + /** + * Tracing errors, warnings and info logs. + */ + private static final ActorTrace ACTOR_TRACE = new ActorTrace(); + + /** + * Gets an instance to the ActorRuntime. There is only 1. + */ + private static volatile ActorRuntime instance; + + /** + * A client used to communicate from the actor to the Dapr runtime. + */ + private final AppToDaprAsyncClient appToDaprAsyncClient; + + /** + * State provider for Dapr. + */ + private final DaprStateAsyncProvider daprStateProvider; + + /** + * Serializes/deserializes objects for Actors. + */ + private final ActorStateSerializer actorSerializer; + + /** + * Map of ActorType --> ActorManager. + */ + private final Map actorManagers; + + /** + * The default constructor. This should not be called directly. + * + * @throws IllegalStateException + */ + private ActorRuntime() throws IllegalStateException { + if (instance != null) { + throw new IllegalStateException("ActorRuntime should only be constructed once"); + } + + this.actorManagers = Collections.synchronizedMap(new HashMap<>()); + this.appToDaprAsyncClient = new AppToDaprClientBuilder().buildAsyncClient(); + this.actorSerializer = new ActorStateSerializer(); + this.daprStateProvider = new DaprStateAsyncProvider(this.appToDaprAsyncClient, this.actorSerializer); } - this.actorManagers = Collections.synchronizedMap(new HashMap<>()); - this.appToDaprAsyncClient = new AppToDaprClientBuilder().buildAsyncClient(); - this.actorSerializer = new ActorStateSerializer(); - this.daprStateProvider = new DaprStateAsyncProvider(this.appToDaprAsyncClient, this.actorSerializer); - } - - /** - * Returns an ActorRuntime object. - * - * @return An ActorRuntime object. - */ - public static ActorRuntime getInstance() { - if (instance == null) { - synchronized (ActorRuntime.class) { + /** + * Returns an ActorRuntime object. + * + * @return An ActorRuntime object. + */ + public static ActorRuntime getInstance() { if (instance == null) { - instance = new ActorRuntime(); + synchronized (ActorRuntime.class) { + if (instance == null) { + instance = new ActorRuntime(); + } + } } - } + + return instance; + } + + /** + * Gets the Actor type names registered with the runtime. + * + * @return Actor type names. + */ + public Collection getRegisteredActorTypes() { + return Collections.unmodifiableCollection(this.actorManagers.keySet()); + } + + /** + * Registers an actor with the runtime. + * + * @param clazz The type of actor. + * @param Actor class type. + * @return Async void task. + */ + public Mono registerActor(Class clazz) { + return registerActor(clazz, null); + } + + /** + * Registers an actor with the runtime. + * + * @param clazz The type of actor. + * @param actorFactory An optional factory to create actors. + * @param Actor class type. + * @return Async void task. + * This can be used for dependency injection into actors. + */ + public Mono registerActor(Class clazz, ActorFactory actorFactory) { + ActorTypeInformation actorTypeInfo = ActorTypeInformation.create(clazz); + + ActorFactory actualActorFactory = actorFactory != null ? actorFactory : new DefaultActorFactory(); + + ActorRuntimeContext context = new ActorRuntimeContext( + this, + this.actorSerializer, + actualActorFactory, + actorTypeInfo, + this.appToDaprAsyncClient, + new DaprStateAsyncProvider(this.appToDaprAsyncClient, this.actorSerializer)); + + // Create ActorManagers, override existing entry if registered again. + this.actorManagers.put(actorTypeInfo.getName(), new ActorManager(context)); + return Mono.empty(); + } + + /** + * Activates an actor for an actor type with given actor id. + * + * @param actorTypeName Actor type name to activate the actor for. + * @param actorId Actor id for the actor to be activated. + * @return Async void task. + */ + public Mono activate(String actorTypeName, String actorId) { + return this.getActorManager(actorTypeName).flatMap(m -> m.activateActor(new ActorId(actorId))); + } + + /** + * Deactivates an actor for an actor type with given actor id. + * + * @param actorTypeName Actor type name to deactivate the actor for. + * @param actorId Actor id for the actor to be deactivated. + * @return Async void task. + */ + public Mono deactivate(String actorTypeName, String actorId) { + return this.getActorManager(actorTypeName).flatMap(m -> m.deactivateActor(new ActorId(actorId))); + } + + /** + * Invokes the specified method for the actor, this is mainly used for cross + * language invocation. + * + * @param actorTypeName Actor type name to invoke the method for. + * @param actorId Actor id for the actor for which method will be invoked. + * @param actorMethodName Method name on actor type which will be invoked. + * @param request Payload for the actor method. + * @return Response for the actor method. + */ + public Mono invoke(String actorTypeName, String actorId, String actorMethodName, String request) { + return this.getActorManager(actorTypeName).flatMap(m -> m.invokeMethod(new ActorId(actorId), actorMethodName, request)); } - return instance; - } - - /** - * Gets the Actor type names registered with the runtime. - * @return Actor type names. - */ - public Collection getRegisteredActorTypes() { - return Collections.unmodifiableCollection(this.actorManagers.keySet()); - } - - /** - * Registers an actor with the runtime. - * - * @param clazz The type of actor. - * @param Actor class type. - * @return Async void task. - */ - public Mono registerActor(Class clazz) { - return registerActor(clazz, null); - } - - /** - * Registers an actor with the runtime. - * - * @param clazz The type of actor. - * @param actorFactory An optional factory to create actors. - * @param Actor class type. - * @return Async void task. - * This can be used for dependency injection into actors. - */ - public Mono registerActor(Class clazz, ActorFactory actorFactory) { - ActorTypeInformation actorTypeInfo = ActorTypeInformation.create(clazz); - - ActorFactory actualActorFactory = actorFactory != null ? actorFactory : new DefaultActorFactory(); - - ActorRuntimeContext context = new ActorRuntimeContext( - this, - this.actorSerializer, - actualActorFactory, - actorTypeInfo, - this.appToDaprAsyncClient, - new DaprStateAsyncProvider(this.appToDaprAsyncClient, this.actorSerializer)); - - // Create ActorManagers, override existing entry if registered again. - this.actorManagers.put(actorTypeInfo.getName(), new ActorManager(context)); - return Mono.empty(); - } - - /** - * Activates an actor for an actor type with given actor id. - * - * @param actorTypeName Actor type name to activate the actor for. - * @param actorId Actor id for the actor to be activated. - * @return Async void task. - */ - public Mono activate(String actorTypeName, String actorId) { - return this.getActorManager(actorTypeName).flatMap(m -> m.activateActor(new ActorId(actorId))); - } - - /** - * Deactivates an actor for an actor type with given actor id. - * - * @param actorTypeName Actor type name to deactivate the actor for. - * @param actorId Actor id for the actor to be deactivated. - * @return Async void task. - */ - public Mono deactivate(String actorTypeName, String actorId) { - return this.getActorManager(actorTypeName).flatMap(m -> m.deactivateActor(new ActorId(actorId))); - } - - /** - * Invokes the specified method for the actor, this is mainly used for cross - * language invocation. - * - * @param actorTypeName Actor type name to invoke the method for. - * @param actorId Actor id for the actor for which method will be invoked. - * @param actorMethodName Method name on actor type which will be invoked. - * @param request Payload for the actor method. - * @return Response for the actor method. - */ - public Mono invoke(String actorTypeName, String actorId, String actorMethodName, String request) { - return this.getActorManager(actorTypeName).flatMap(m -> m.invokeMethod(new ActorId(actorId), actorMethodName, request)); - } - - /** - * Fires a reminder for the Actor. - * - * @param actorTypeName Actor type name to invoke the method for. - * @param actorId Actor id for the actor for which method will be invoked. - * @param reminderName The name of reminder provided during registration. - * @param request Payload for the actor method - * @return Async void task. - */ - public Mono invokeReminder(String actorTypeName, String actorId, String reminderName, String request) { - return this.getActorManager(actorTypeName).flatMap(m -> m.invokeReminder(new ActorId(actorId), reminderName, request)); - } - - /** - * Fires a timer for the Actor. - * - * @param actorTypeName Actor type name to invoke the method for. - * @param actorId Actor id for the actor for which method will be invoked. - * @param timerName The name of timer provided during registration. - * @return Async void task. - */ - public Mono invokeTimer(String actorTypeName, String actorId, String timerName) { - return this.getActorManager(actorTypeName).flatMap(m -> m.invokeTimer(new ActorId(actorId), timerName)); - } - - /** - * Finds the actor manager or errors out. - * @param actorTypeName Actor type for the actor manager to be found. - * @return Actor manager or error if not found. - */ - private Mono getActorManager(String actorTypeName) { - ActorManager actorManager = this.actorManagers.get(actorTypeName); - - try { - if (actorManager == null) { - String errorMsg = String.format("Actor type %s is not registered with Actor runtime.", actorTypeName); - - ACTOR_TRACE.writeError(TRACE_TYPE, actorTypeName, "Actor type is not registered with runtime."); - - throw new IllegalStateException(errorMsg); - } - } catch (IllegalStateException e) { - return Mono.error(e); + /** + * Fires a reminder for the Actor. + * + * @param actorTypeName Actor type name to invoke the method for. + * @param actorId Actor id for the actor for which method will be invoked. + * @param reminderName The name of reminder provided during registration. + * @param params Params for the reminder. + * @return Async void task. + */ + public Mono invokeReminder(String actorTypeName, String actorId, String reminderName, String params) { + return this.getActorManager(actorTypeName).flatMap(m -> m.invokeReminder(new ActorId(actorId), reminderName, params)); } - return Mono.just(actorManager); - } + /** + * Fires a timer for the Actor. + * + * @param actorTypeName Actor type name to invoke the method for. + * @param actorId Actor id for the actor for which method will be invoked. + * @param timerName The name of timer provided during registration. + * @return Async void task. + */ + public Mono invokeTimer(String actorTypeName, String actorId, String timerName) { + return this.getActorManager(actorTypeName).flatMap(m -> m.invokeTimer(new ActorId(actorId), timerName)); + } + + /** + * Finds the actor manager or errors out. + * + * @param actorTypeName Actor type for the actor manager to be found. + * @return Actor manager or error if not found. + */ + private Mono getActorManager(String actorTypeName) { + ActorManager actorManager = this.actorManagers.get(actorTypeName); + + try { + if (actorManager == null) { + String errorMsg = String.format("Actor type %s is not registered with Actor runtime.", actorTypeName); + + ACTOR_TRACE.writeError(TRACE_TYPE, actorTypeName, "Actor type is not registered with runtime."); + + throw new IllegalStateException(errorMsg); + } + } catch (IllegalStateException e) { + return Mono.error(e); + } + + return Mono.just(actorManager); + } } \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java index b61fa7c0ec..c3533de36a 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java @@ -9,115 +9,131 @@ /** * Provides the context for the Actor's runtime. + * * @param Actor's type for the context. */ public class ActorRuntimeContext { - /** - * Runtime. - */ - private final ActorRuntime actorRuntime; - - /** - * Serializer. - */ - private final ActorStateSerializer actorSerializer; - - /** - * Actor factory. - */ - private final ActorFactory actorFactory; - - /** - * Information of the Actor's type. - */ - private final ActorTypeInformation actorTypeInformation; - - /** - * Trace for Actor logs. - */ - private final ActorTrace actorTrace; - - /** - * Client to communicate to Dapr's API. - */ - private final AppToDaprAsyncClient daprClient; - - /** - * State provider for given Actor Type. - */ - private final DaprStateAsyncProvider stateProvider; - - /** - * Instantiates a new runtime context for the Actor type. - * @param actorRuntime Runtime. - * @param actorSerializer Serializer. - * @param actorFactory Factory for Actors. - * @param actorTypeInformation Information for Actor's type. - * @param daprClient Client to communicate to Dapr. - * @param stateProvider State provider for given Actor's type. - */ - ActorRuntimeContext(ActorRuntime actorRuntime, - ActorStateSerializer actorSerializer, - ActorFactory actorFactory, - ActorTypeInformation actorTypeInformation, - AppToDaprAsyncClient daprClient, DaprStateAsyncProvider stateProvider) { - this.actorRuntime = actorRuntime; - this.actorSerializer = actorSerializer; - this.actorFactory = actorFactory; - this.actorTypeInformation = actorTypeInformation; - this.actorTrace = new ActorTrace(); - this.daprClient = daprClient; - this.stateProvider = stateProvider; - } - - /** - * Gets the Actor's runtime. - * @return Actor's runtime. - */ - ActorRuntime getActorRuntime() { - return this.actorRuntime; - } - - /** - * Gets the Actor's serializer. - * @return Actor's serializer. - */ - ActorStateSerializer getActorSerializer() { - return this.actorSerializer; - } - - /** - * Gets the Actor's serializer. - * @return Actor's serializer. - */ - ActorFactory getActorFactory() { - return this.actorFactory; - } - - /** - * Gets the information about the Actor's type. - * @return Information about the Actor's type. - */ - ActorTypeInformation getActorTypeInformation() { - return this.actorTypeInformation; - } - - /** - * Gets the trace for Actor logs. - * @return Trace for Actor logs. - */ - ActorTrace getActorTrace() { return this.actorTrace; } - - /** - * Gets the client to communicate to Dapr's API. - * @return Client to communicate to Dapr's API. - */ - AppToDaprAsyncClient getDaprClient() { return this.daprClient; } - - /** - * Gets the state provider for given Actor's type. - * @return State provider for given Actor's type. - */ - DaprStateAsyncProvider getStateProvider() { return stateProvider; } + /** + * Runtime. + */ + private final ActorRuntime actorRuntime; + + /** + * Serializer. + */ + private final ActorStateSerializer actorSerializer; + + /** + * Actor factory. + */ + private final ActorFactory actorFactory; + + /** + * Information of the Actor's type. + */ + private final ActorTypeInformation actorTypeInformation; + + /** + * Trace for Actor logs. + */ + private final ActorTrace actorTrace; + + /** + * Client to communicate to Dapr's API. + */ + private final AppToDaprAsyncClient daprClient; + + /** + * State provider for given Actor Type. + */ + private final DaprStateAsyncProvider stateProvider; + + /** + * Instantiates a new runtime context for the Actor type. + * + * @param actorRuntime Runtime. + * @param actorSerializer Serializer. + * @param actorFactory Factory for Actors. + * @param actorTypeInformation Information for Actor's type. + * @param daprClient Client to communicate to Dapr. + * @param stateProvider State provider for given Actor's type. + */ + ActorRuntimeContext(ActorRuntime actorRuntime, + ActorStateSerializer actorSerializer, + ActorFactory actorFactory, + ActorTypeInformation actorTypeInformation, + AppToDaprAsyncClient daprClient, + DaprStateAsyncProvider stateProvider) { + this.actorRuntime = actorRuntime; + this.actorSerializer = actorSerializer; + this.actorFactory = actorFactory; + this.actorTypeInformation = actorTypeInformation; + this.actorTrace = new ActorTrace(); + this.daprClient = daprClient; + this.stateProvider = stateProvider; + } + + /** + * Gets the Actor's runtime. + * + * @return Actor's runtime. + */ + ActorRuntime getActorRuntime() { + return this.actorRuntime; + } + + /** + * Gets the Actor's serializer. + * + * @return Actor's serializer. + */ + ActorStateSerializer getActorSerializer() { + return this.actorSerializer; + } + + /** + * Gets the Actor's serializer. + * + * @return Actor's serializer. + */ + ActorFactory getActorFactory() { + return this.actorFactory; + } + + /** + * Gets the information about the Actor's type. + * + * @return Information about the Actor's type. + */ + ActorTypeInformation getActorTypeInformation() { + return this.actorTypeInformation; + } + + /** + * Gets the trace for Actor logs. + * + * @return Trace for Actor logs. + */ + ActorTrace getActorTrace() { + return this.actorTrace; + } + + /** + * Gets the client to communicate to Dapr's API. + * + * @return Client to communicate to Dapr's API. + */ + AppToDaprAsyncClient getDaprClient() { + return this.daprClient; + } + + /** + * Gets the state provider for given Actor's type. + * + * @return State provider for given Actor's type. + */ + DaprStateAsyncProvider getStateProvider() { + return stateProvider; + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChange.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChange.java index bbb5ef52be..f328cc9393 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChange.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChange.java @@ -10,55 +10,59 @@ */ public final class ActorStateChange { - /** - * Name of the state being changed. - */ - private final String stateName; + /** + * Name of the state being changed. + */ + private final String stateName; - /** - * New value for the state being changed. - */ - private final Object value; + /** + * New value for the state being changed. + */ + private final Object value; - /** - * Type of change {@link ActorStateChangeKind}. - */ - private final ActorStateChangeKind changeKind; + /** + * Type of change {@link ActorStateChangeKind}. + */ + private final ActorStateChangeKind changeKind; - /** - * Creates an actor state change. - * @param stateName Name of the state being changed. - * @param value New value for the state being changed. - * @param changeKind Kind of change. - */ - ActorStateChange(String stateName, Object value, ActorStateChangeKind changeKind) { - this.stateName = stateName; - this.value = value; - this.changeKind = changeKind; - } + /** + * Creates an actor state change. + * + * @param stateName Name of the state being changed. + * @param value New value for the state being changed. + * @param changeKind Kind of change. + */ + ActorStateChange(String stateName, Object value, ActorStateChangeKind changeKind) { + this.stateName = stateName; + this.value = value; + this.changeKind = changeKind; + } - /** - * Gets the name of the state being changed. - * @return Name of the state. - */ - String getStateName() { - return stateName; - } + /** + * Gets the name of the state being changed. + * + * @return Name of the state. + */ + String getStateName() { + return stateName; + } - /** - * Gets the new value of the state being changed. - * @return New value. - */ - Object getValue() { - return value; - } + /** + * Gets the new value of the state being changed. + * + * @return New value. + */ + Object getValue() { + return value; + } - /** - * Gets the kind of change. - * @return Kind of change. - */ - ActorStateChangeKind getChangeKind() { - return changeKind; - } + /** + * Gets the kind of change. + * + * @return Kind of change. + */ + ActorStateChangeKind getChangeKind() { + return changeKind; + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChangeKind.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChangeKind.java index 103b46f09b..883d424fad 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChangeKind.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateChangeKind.java @@ -10,45 +10,47 @@ */ public enum ActorStateChangeKind { - /** - * No change in state. - */ - NONE(""), - - /** - * State needs to be added. - */ - ADD("upsert"), - - /** - * State needs to be updated. - */ - UPDATE("upsert"), - - /** - * State needs to be removed. - */ - REMOVE("delete"); - - /** - * Operation name in Dapr's state management. - */ - private final String daprStateChangeOperation; - - /** - * Creates a kind of actor state change. - * @param daprStateChangeOperation Equivalent operation name Dapr's state management - */ - ActorStateChangeKind(String daprStateChangeOperation) { - this.daprStateChangeOperation = daprStateChangeOperation; - } - - /** - * Gets equivalent operation name Dapr's state management - * @return Equivalent operation name Dapr's state management - */ - String getDaprStateChangeOperation() { - return daprStateChangeOperation; - } + /** + * No change in state. + */ + NONE(""), + + /** + * State needs to be added. + */ + ADD("upsert"), + + /** + * State needs to be updated. + */ + UPDATE("upsert"), + + /** + * State needs to be removed. + */ + REMOVE("delete"); + + /** + * Operation name in Dapr's state management. + */ + private final String daprStateChangeOperation; + + /** + * Creates a kind of actor state change. + * + * @param daprStateChangeOperation Equivalent operation name Dapr's state management + */ + ActorStateChangeKind(String daprStateChangeOperation) { + this.daprStateChangeOperation = daprStateChangeOperation; + } + + /** + * Gets equivalent operation name Dapr's state management + * + * @return Equivalent operation name Dapr's state management + */ + String getDaprStateChangeOperation() { + return daprStateChangeOperation; + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateManager.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateManager.java index 165aa7f511..3f421958e3 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateManager.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateManager.java @@ -12,296 +12,306 @@ /** * Manages state changes of a given Actor instance. - * + *

* All changes are cached in-memory until save() is called. */ class ActorStateManager { - /** - * Provides states using a state store. - */ - private final DaprStateAsyncProvider stateProvider; - - /** - * Name of the Actor's type. - */ - private final String actorTypeName; - - /** - * Actor's identifier. - */ - private final ActorId actorId; - - /** - * Cache of state changes in this Actor's instance. - */ - private final Map stateChangeTracker; - - /** - * Instantiates a new state manager for the given Actor's instance. - * @param stateProvider State store provider. - * @param actorTypeName Name of Actor's type. - * @param actorId Actor's identifier. - */ - ActorStateManager(DaprStateAsyncProvider stateProvider, String actorTypeName, ActorId actorId) { - this.stateProvider = stateProvider; - this.actorTypeName = actorTypeName; - this.actorId = actorId; - this.stateChangeTracker = new HashMap<>(); - } - - /** - * Adds a given key/value to the Actor's state store's cache. - * @param stateName Name of the state being added. - * @param value Value to be added. - * @param Type of the object being added. - * @return Asynchronous void operation. - */ - Mono add(String stateName, T value) { - try { - if (stateName == null) { - throw new IllegalArgumentException("State's name cannot be null."); - } - - if (this.stateChangeTracker.containsKey(stateName)) { - StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - - if (metadata.kind == ActorStateChangeKind.REMOVE) { - this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.UPDATE, value)); - return Mono.empty(); - } + /** + * Provides states using a state store. + */ + private final DaprStateAsyncProvider stateProvider; - throw new IllegalStateException("Duplicate cached state: " + stateName); - } + /** + * Name of the Actor's type. + */ + private final String actorTypeName; - return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) - .flatMap(exists -> { - if (exists) { - throw new IllegalStateException("Duplicate state: " + stateName); - } + /** + * Actor's identifier. + */ + private final ActorId actorId; - this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.ADD, value)); - return Mono.empty(); - }); - } catch (Exception e) { - return Mono.error(e); - } - } - - /** - * Fetches the most recent value for the given state, including cached value. - * @param stateName Name of the state. - * @param clazz Class type for the value being fetched. - * @param Type being fetched. - * @return Asynchronous response with fetched object. - */ - Mono get(String stateName, Class clazz) { - try { - if (stateName == null) { - throw new IllegalArgumentException("State's name cannot be null."); - } - - if (this.stateChangeTracker.containsKey(stateName)) { - StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - - if (metadata.kind == ActorStateChangeKind.REMOVE) { - throw new NoSuchElementException("State is marked for removal: " + stateName); - } + /** + * Cache of state changes in this Actor's instance. + */ + private final Map stateChangeTracker; - return Mono.just((T) metadata.value); - } - - return this.stateProvider.load(this.actorTypeName, this.actorId, stateName, clazz) - .switchIfEmpty(Mono.error(new NoSuchElementException("State not found: " + stateName))) - .map(v -> { - this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, v)); - return (T)v; - }); - } catch (Exception e) { - return Mono.error(e); + /** + * Instantiates a new state manager for the given Actor's instance. + * + * @param stateProvider State store provider. + * @param actorTypeName Name of Actor's type. + * @param actorId Actor's identifier. + */ + ActorStateManager(DaprStateAsyncProvider stateProvider, String actorTypeName, ActorId actorId) { + this.stateProvider = stateProvider; + this.actorTypeName = actorTypeName; + this.actorId = actorId; + this.stateChangeTracker = new HashMap<>(); } - } - - /** - * Updates a given key/value pair in the state store's cache. - * @param stateName Name of the state being updated. - * @param value Value to be set for given state. - * @param Type of the value being set. - * @return Asynchronous void result. - */ - Mono set(String stateName, T value) { - try { - if (stateName == null) { - throw new IllegalArgumentException("State's name cannot be null."); - } - - if (this.stateChangeTracker.containsKey(stateName)) { - StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - - ActorStateChangeKind kind = metadata.kind; - if ((kind == ActorStateChangeKind.NONE) || (kind == ActorStateChangeKind.REMOVE)) { - kind = ActorStateChangeKind.UPDATE; - } - this.stateChangeTracker.put(stateName, new StateChangeMetadata(kind, value)); - return Mono.empty(); - } - - return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) - .map(exists -> { - this.stateChangeTracker.put(stateName, - new StateChangeMetadata(exists ? ActorStateChangeKind.UPDATE : ActorStateChangeKind.ADD, value)); - return exists; - }) - .then(); - } catch (Exception e) { - return Mono.error(e); - } - } - - /** - * Removes a given state from state store's cache. - * @param stateName State being stored. - * @return Asynchronous void result. - */ - Mono remove(String stateName) { - try { - if (stateName == null) { - throw new IllegalArgumentException("State's name cannot be null."); - } - - if (this.stateChangeTracker.containsKey(stateName)) { - StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - - if (metadata.kind == ActorStateChangeKind.REMOVE) { - return Mono.empty(); + /** + * Adds a given key/value to the Actor's state store's cache. + * + * @param stateName Name of the state being added. + * @param value Value to be added. + * @param Type of the object being added. + * @return Asynchronous void operation. + */ + Mono add(String stateName, T value) { + try { + if (stateName == null) { + throw new IllegalArgumentException("State's name cannot be null."); + } + + if (this.stateChangeTracker.containsKey(stateName)) { + StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); + + if (metadata.kind == ActorStateChangeKind.REMOVE) { + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.UPDATE, value)); + return Mono.empty(); + } + + throw new IllegalStateException("Duplicate cached state: " + stateName); + } + + return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) + .flatMap(exists -> { + if (exists) { + throw new IllegalStateException("Duplicate state: " + stateName); + } + + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.ADD, value)); + return Mono.empty(); + }); + } catch (Exception e) { + return Mono.error(e); } + } - if (metadata.kind == ActorStateChangeKind.ADD) { - this.stateChangeTracker.remove(stateName); - return Mono.empty(); + /** + * Fetches the most recent value for the given state, including cached value. + * + * @param stateName Name of the state. + * @param clazz Class type for the value being fetched. + * @param Type being fetched. + * @return Asynchronous response with fetched object. + */ + Mono get(String stateName, Class clazz) { + try { + if (stateName == null) { + throw new IllegalArgumentException("State's name cannot be null."); + } + + if (this.stateChangeTracker.containsKey(stateName)) { + StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); + + if (metadata.kind == ActorStateChangeKind.REMOVE) { + throw new NoSuchElementException("State is marked for removal: " + stateName); + } + + return Mono.just((T) metadata.value); + } + + return this.stateProvider.load(this.actorTypeName, this.actorId, stateName, clazz) + .switchIfEmpty(Mono.error(new NoSuchElementException("State not found: " + stateName))) + .map(v -> { + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, v)); + return (T) v; + }); + } catch (Exception e) { + return Mono.error(e); } + } - this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, null)); - return Mono.empty(); - } - - return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) - .filter(exists -> exists) - .map(exists -> { - this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, null)); - return exists; - }) - .then(); - } catch (Exception e) { - return Mono.error(e); + /** + * Updates a given key/value pair in the state store's cache. + * + * @param stateName Name of the state being updated. + * @param value Value to be set for given state. + * @param Type of the value being set. + * @return Asynchronous void result. + */ + Mono set(String stateName, T value) { + try { + if (stateName == null) { + throw new IllegalArgumentException("State's name cannot be null."); + } + + if (this.stateChangeTracker.containsKey(stateName)) { + StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); + + ActorStateChangeKind kind = metadata.kind; + if ((kind == ActorStateChangeKind.NONE) || (kind == ActorStateChangeKind.REMOVE)) { + kind = ActorStateChangeKind.UPDATE; + } + + this.stateChangeTracker.put(stateName, new StateChangeMetadata(kind, value)); + return Mono.empty(); + } + + return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) + .map(exists -> { + this.stateChangeTracker.put(stateName, + new StateChangeMetadata(exists ? ActorStateChangeKind.UPDATE : ActorStateChangeKind.ADD, value)); + return exists; + }) + .then(); + } catch (Exception e) { + return Mono.error(e); + } } - } - - /** - * Checks if a given state exists in state store or cache. - * @param stateName State being checked. - * @return Asynchronous boolean result indicating whether state is present. - */ - Mono contains(String stateName) { - try { - if (stateName == null) { - throw new IllegalArgumentException("State's name cannot be null."); - } - - if (this.stateChangeTracker.containsKey(stateName)) { - StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - - if (metadata.kind == ActorStateChangeKind.REMOVE) { - return Mono.just(false); + + /** + * Removes a given state from state store's cache. + * + * @param stateName State being stored. + * @return Asynchronous void result. + */ + Mono remove(String stateName) { + try { + if (stateName == null) { + throw new IllegalArgumentException("State's name cannot be null."); + } + + if (this.stateChangeTracker.containsKey(stateName)) { + StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); + + if (metadata.kind == ActorStateChangeKind.REMOVE) { + return Mono.empty(); + } + + if (metadata.kind == ActorStateChangeKind.ADD) { + this.stateChangeTracker.remove(stateName); + return Mono.empty(); + } + + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, null)); + return Mono.empty(); + } + + return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName) + .filter(exists -> exists) + .map(exists -> { + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.REMOVE, null)); + return exists; + }) + .then(); + } catch (Exception e) { + return Mono.error(e); } + } - return Mono.just(true); - } + /** + * Checks if a given state exists in state store or cache. + * + * @param stateName State being checked. + * @return Asynchronous boolean result indicating whether state is present. + */ + Mono contains(String stateName) { + try { + if (stateName == null) { + throw new IllegalArgumentException("State's name cannot be null."); + } - return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName); - } catch (Exception e) { - return Mono.error(e); - } - } - - /** - * Saves all changes to state store. - * @return Asynchronous void result. - */ - Mono save() { - if (this.stateChangeTracker.isEmpty()) { - return Mono.empty(); - } + if (this.stateChangeTracker.containsKey(stateName)) { + StateChangeMetadata metadata = this.stateChangeTracker.get(stateName); - List changes = new ArrayList<>(); - List removed = new ArrayList<>(); - for (Map.Entry tuple : this.stateChangeTracker.entrySet()) { - if (tuple.getValue().kind == ActorStateChangeKind.NONE) { - continue; - } + if (metadata.kind == ActorStateChangeKind.REMOVE) { + return Mono.just(false); + } - if (tuple.getValue().kind == ActorStateChangeKind.REMOVE) { - removed.add(tuple.getKey()); - } + return Mono.just(true); + } - changes.add(new ActorStateChange(tuple.getKey(), tuple.getValue().value, tuple.getValue().kind)); + return this.stateProvider.contains(this.actorTypeName, this.actorId, stateName); + } catch (Exception e) { + return Mono.error(e); + } } - return this.stateProvider.apply(this.actorTypeName, this.actorId, changes.toArray(new ActorStateChange[0])) - .then(this.flush()); - } - - /** - * Clears all changes not yet saved to state store. - * @return - */ - Mono clear() { - this.stateChangeTracker.clear(); - return Mono.empty(); - } - - /** - * Commits the current cached values after successful save. - * @return - */ - private Mono flush() { - for (Map.Entry tuple : this.stateChangeTracker.entrySet()) { - String stateName = tuple.getKey(); - if (tuple.getValue().kind == ActorStateChangeKind.REMOVE) { - this.stateChangeTracker.remove(stateName); - } else { - StateChangeMetadata metadata = new StateChangeMetadata(ActorStateChangeKind.NONE, tuple.getValue().value); - this.stateChangeTracker.put(stateName, metadata); - } - } + /** + * Saves all changes to state store. + * + * @return Asynchronous void result. + */ + Mono save() { + if (this.stateChangeTracker.isEmpty()) { + return Mono.empty(); + } + + List changes = new ArrayList<>(); + List removed = new ArrayList<>(); + for (Map.Entry tuple : this.stateChangeTracker.entrySet()) { + if (tuple.getValue().kind == ActorStateChangeKind.NONE) { + continue; + } + + if (tuple.getValue().kind == ActorStateChangeKind.REMOVE) { + removed.add(tuple.getKey()); + } - return Mono.empty(); - } + changes.add(new ActorStateChange(tuple.getKey(), tuple.getValue().value, tuple.getValue().kind)); + } - /** - * Internal class to represent value and change kind. - */ - private static final class StateChangeMetadata { + return this.stateProvider.apply(this.actorTypeName, this.actorId, changes.toArray(new ActorStateChange[0])) + .then(this.flush()); + } /** - * Kind of change cached. + * Clears all changes not yet saved to state store. + * + * @return */ - private final ActorStateChangeKind kind; + Mono clear() { + this.stateChangeTracker.clear(); + return Mono.empty(); + } /** - * Value cached. + * Commits the current cached values after successful save. + * + * @return */ - private final Object value; + private Mono flush() { + for (Map.Entry tuple : this.stateChangeTracker.entrySet()) { + String stateName = tuple.getKey(); + if (tuple.getValue().kind == ActorStateChangeKind.REMOVE) { + this.stateChangeTracker.remove(stateName); + } else { + StateChangeMetadata metadata = new StateChangeMetadata(ActorStateChangeKind.NONE, tuple.getValue().value); + this.stateChangeTracker.put(stateName, metadata); + } + } + + return Mono.empty(); + } /** - * Creates a new instance of the metadata on state change. - * @param kind Kind of change. - * @param value Value to be set. + * Internal class to represent value and change kind. */ - private StateChangeMetadata(ActorStateChangeKind kind, Object value) { - this.kind = kind; - this.value = value; + private static final class StateChangeMetadata { + + /** + * Kind of change cached. + */ + private final ActorStateChangeKind kind; + + /** + * Value cached. + */ + private final Object value; + + /** + * Creates a new instance of the metadata on state change. + * + * @param kind Kind of change. + * @param value Value to be set. + */ + private StateChangeMetadata(ActorStateChangeKind kind, Object value) { + this.kind = kind; + this.value = value; + } } - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java index 8d5a790017..a5ec021793 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java @@ -4,136 +4,196 @@ */ package io.dapr.actors.runtime; -import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.dapr.actors.utils.ObjectSerializer; +import io.dapr.utils.ObjectSerializer; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.time.Duration; /** * Serializes and deserializes an object. */ -class ActorStateSerializer extends ObjectSerializer { - - /** - * Shared Json Factory as per Jackson's documentation, used only for this class. - */ - private static final JsonFactory JSON_FACTORY = new JsonFactory(); - - /** - * Shared Json serializer/deserializer as per Jackson's documentation. - */ - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - /** - * Serializes a given state object into byte array. - * - * @param state State object to be serialized. - * @return Array of bytes[] with the serialized content. - * @throws IOException - */ - public String serialize(T state) throws IOException { - if (state == null) { - return null; +public class ActorStateSerializer extends ObjectSerializer { + + /** + * {@inheritDoc} + */ + @Override + public String serialize(T state) throws IOException { + if (state == null) { + return null; + } + + if (state.getClass() == ActorTimer.class) { + // Special serializer for this internal classes. + return serialize((ActorTimer) state); + } + + if (state.getClass() == ActorReminderParams.class) { + // Special serializer for this internal classes. + return serialize((ActorReminderParams) state); + } + + // Is not an special case. + return super.serialize(state); } - if (state.getClass() == ActorTimer.class) { - // Special serializer for this internal classes. - return serialize((ActorTimer) state); + /** + * {@inheritDoc} + */ + @Override + public T deserialize(Object value, Class clazz) throws IOException { + if (clazz == ActorReminderParams.class) { + // Special serializer for this internal classes. + return (T) deserializeActorReminder(value); + } + + // Is not one the special cases. + return super.deserialize(value, clazz); } - if (state.getClass() == ActorReminderParams.class) { - // Special serializer for this internal classes. - return serialize((ActorReminderParams) state); + /** + * Extracts the response object from the Actor's method result. + * + * @param response String returned by API. + * @param clazz Expected response class. + * @param Expected response type. + * @return Response object, null or RuntimeException. + */ + public T unwrapMethodResponse(final String response, Class clazz) { + if (response == null) { + return null; + } + + try { + JsonNode root = OBJECT_MAPPER.readTree(response); + if (root == null) { + return null; + } + + JsonNode dataNode = root.get("data"); + if (dataNode == null) { + return null; + } + + byte[] data = dataNode.binaryValue(); + if (data == null) { + return null; + } + + return this.deserialize(new String(data, StandardCharsets.UTF_8), clazz); + } catch (IOException e) { + // Wrap it to make Mono happy. + throw new RuntimeException(e); + } } - // Is not an special case. - return super.serialize(state); - } - - /** - * {@inheritDoc} - * Deserializes the byte array into the original object. - * - * @param value String to be parsed. - * @param clazz Type of the object being deserialized. - * @param Generic type of the object being deserialized. - * @return Object of type T. - * @throws IOException - */ - @Override - public T deserialize(String value, Class clazz) throws IOException { - if (clazz == ActorReminderParams.class) { - // Special serializer for this internal classes. - return (T) deserializeActorReminder(value); + /** + * Builds the request to invoke an API for Actors. + * + * @param request Request object for the original Actor's method. + * @param Type for the original Actor's method request. + * @return String to be sent to Dapr's API. + * @throws IOException In case it cannot generate String. + */ + public String wrapMethodRequest(final T request) throws IOException { + if (request == null) { + return null; + } + + String json = this.serialize(request); + + try (Writer writer = new StringWriter()) { + JsonGenerator generator = JSON_FACTORY.createGenerator(writer); + generator.writeStartObject(); + if (json != null) { + generator.writeBinaryField("data", json.getBytes()); + } + generator.writeEndObject(); + generator.close(); + writer.flush(); + return writer.toString(); + } } - // Is not one the special cases. - return super.deserialize(value, clazz); - } - - /** - * Faster serialization for Actor's timer. - * @param timer Timer to be serialized. - * @return JSON String. - * @throws IOException If cannot generate JSON. - */ - private static String serialize(ActorTimer timer) throws IOException { - try (Writer writer = new StringWriter()) { - JsonGenerator generator = JSON_FACTORY.createGenerator(writer); - generator.writeStartObject(); - generator.writeStringField("dueTime", DurationUtils.ConvertDurationToDaprFormat(timer.getDueTime())); - generator.writeStringField("period", DurationUtils.ConvertDurationToDaprFormat(timer.getPeriod())); - generator.writeEndObject(); - generator.close(); - writer.flush(); - return writer.toString(); + /** + * Faster serialization for params of Actor's timer. + * + * @param timer Timer's to be serialized. + * @return JSON String. + * @throws IOException If cannot generate JSON. + */ + private String serialize(ActorTimer timer) throws IOException { + if (timer == null) { + return null; + } + + try (Writer writer = new StringWriter()) { + JsonGenerator generator = JSON_FACTORY.createGenerator(writer); + generator.writeStartObject(); + generator.writeStringField("dueTime", DurationUtils.ConvertDurationToDaprFormat(timer.getDueTime())); + generator.writeStringField("period", DurationUtils.ConvertDurationToDaprFormat(timer.getPeriod())); + generator.writeStringField("callback", timer.getCallback()); + if (timer.getState() != null) { + generator.writeStringField("data", this.serialize(timer.getState())); + } + generator.writeEndObject(); + generator.close(); + writer.flush(); + return writer.toString(); + } } - } - - /** - * Faster serialization for Actor's reminder. - * @param reminder Reminder to be serialized. - * @return JSON String. - * @throws IOException If cannot generate JSON. - */ - private static String serialize(ActorReminderParams reminder) throws IOException { - try (Writer writer = new StringWriter()) { - JsonGenerator generator = JSON_FACTORY.createGenerator(writer); - generator.writeStartObject(); - generator.writeStringField("dueTime", DurationUtils.ConvertDurationToDaprFormat(reminder.getDueTime())); - generator.writeStringField("period", DurationUtils.ConvertDurationToDaprFormat(reminder.getPeriod())); - if (reminder.getData() != null) { - generator.writeStringField("data", reminder.getData()); - } - generator.writeEndObject(); - generator.close(); - writer.flush(); - return writer.toString(); - } - } - - /** - * Deserializes an Actor Reminder. - * @param value String to be deserialized. - * @return Actor Reminder. - * @throws IOException If cannot parse JSON. - */ - private static ActorReminderParams deserializeActorReminder(String value) throws IOException { - if (value == null) { - return null; + + /** + * Faster serialization for Actor's reminder. + * + * @param reminder Reminder to be serialized. + * @return JSON String. + * @throws IOException If cannot generate JSON. + */ + private String serialize(ActorReminderParams reminder) throws IOException { + try (Writer writer = new StringWriter()) { + JsonGenerator generator = JSON_FACTORY.createGenerator(writer); + generator.writeStartObject(); + generator.writeStringField("dueTime", DurationUtils.ConvertDurationToDaprFormat(reminder.getDueTime())); + generator.writeStringField("period", DurationUtils.ConvertDurationToDaprFormat(reminder.getPeriod())); + if (reminder.getData() != null) { + generator.writeStringField("data", reminder.getData()); + } + generator.writeEndObject(); + generator.close(); + writer.flush(); + return writer.toString(); + } } - JsonNode node = OBJECT_MAPPER.readTree(value); - Duration dueTime = DurationUtils.ConvertDurationFromDaprFormat(node.get("dueTime").asText()); - Duration period = DurationUtils.ConvertDurationFromDaprFormat(node.get("period").asText()); - String data = node.get("data") != null ? node.get("data").asText() : null; + /** + * Deserializes an Actor Reminder. + * + * @param value Content to be deserialized. + * @return Actor Reminder. + * @throws IOException If cannot parse JSON. + */ + private ActorReminderParams deserializeActorReminder(Object value) throws IOException { + if (value == null) { + return null; + } + + JsonNode node; + if (value instanceof byte[]) { + node = OBJECT_MAPPER.readTree((byte[]) value); + } else { + node = OBJECT_MAPPER.readTree(value.toString()); + } + Duration dueTime = DurationUtils.ConvertDurationFromDaprFormat(node.get("dueTime").asText()); + Duration period = DurationUtils.ConvertDurationFromDaprFormat(node.get("period").asText()); + String data = node.get("data") != null ? node.get("data").asText() : null; + + return new ActorReminderParams(data, dueTime, period); + } - return new ActorReminderParams(data, dueTime, period); - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorTimer.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorTimer.java index fad5214132..6b91121a93 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorTimer.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorTimer.java @@ -9,107 +9,108 @@ /** * Represents the timer set on an Actor, to be called once after due time and then every period. + * * @param State type. */ final class ActorTimer { - /** - * Actor that owns this timer. - */ - private final AbstractActor owner; - - /** - * Name of this timer. - */ - private String name; - - /** - * Name of the method to be called for this timer. - */ - private String methodName; - - /** - * State to be sent in the timer. - */ - private T state; - - /** - * Due time for the timer's first trigger. - */ - private Duration dueTime; - - /** - * Period at which the timer will be triggered. - */ - private Duration period; - - /** - * Instantiates a new Actor Timer. - * - * @param owner The Actor that owns this timer. The timer callback will be fired for this Actor. - * @param timerName The name of the timer. - * @param methodName The name of the method to be called for this timer. - * @param state information to be used by the callback method - * @param dueTime the time when timer is first due. - * @param period the periodic time when timer will be invoked. - */ - ActorTimer(AbstractActor owner, - String timerName, - String methodName, - T state, - Duration dueTime, - Duration period) { - this.owner = owner; - this.name = timerName; - this.methodName = methodName; - this.state = state; - this.dueTime = dueTime; - this.period = period; - } - - /** - * Gets the name of the Timer. The name is unique per actor. - * - * @return The name of the timer. - */ - public String getName() { - return this.name; - } - - /** - * Gets the name of the method for this Timer. - * - * @return The name of the method for this timer. - */ - public String getMethodName() { - return this.methodName; - } - - /** - * Gets the time when timer is first due. - * - * @return Time as Duration when timer is first due. - */ - public Duration getDueTime() { - return this.dueTime; - } - - /** - * Gets the periodic time when timer will be invoked. - * - * @return Periodic time as Duration when timer will be invoked. - */ - public Duration getPeriod() { - return this.period; - } - - /** - * Gets state containing information to be used by the callback method, or null. - * - * @return State containing information to be used by the callback method, or null. - */ - public T getState() { - return this.state; - } + /** + * Actor that owns this timer. + */ + private final AbstractActor owner; + + /** + * Name of this timer. + */ + private String name; + + /** + * Name of the method to be called for this timer. + */ + private String callback; + + /** + * State to be sent in the timer. + */ + private T state; + + /** + * Due time for the timer's first trigger. + */ + private Duration dueTime; + + /** + * Period at which the timer will be triggered. + */ + private Duration period; + + /** + * Instantiates a new Actor Timer. + * + * @param owner The Actor that owns this timer. The timer callback will be fired for this Actor. + * @param timerName The name of the timer. + * @param callback The name of the method to be called for this timer. + * @param state information to be used by the callback method + * @param dueTime the time when timer is first due. + * @param period the periodic time when timer will be invoked. + */ + ActorTimer(AbstractActor owner, + String timerName, + String callback, + T state, + Duration dueTime, + Duration period) { + this.owner = owner; + this.name = timerName; + this.callback = callback; + this.state = state; + this.dueTime = dueTime; + this.period = period; + } + + /** + * Gets the name of the Timer. The name is unique per actor. + * + * @return The name of the timer. + */ + public String getName() { + return this.name; + } + + /** + * Gets the name of the method for this Timer. + * + * @return The name of the method for this timer. + */ + public String getCallback() { + return this.callback; + } + + /** + * Gets the time when timer is first due. + * + * @return Time as Duration when timer is first due. + */ + public Duration getDueTime() { + return this.dueTime; + } + + /** + * Gets the periodic time when timer will be invoked. + * + * @return Periodic time as Duration when timer will be invoked. + */ + public Duration getPeriod() { + return this.period; + } + + /** + * Gets state containing information to be used by the callback method, or null. + * + * @return State containing information to be used by the callback method, or null. + */ + public T getState() { + return this.state; + } } \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorTimerParams.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorTimerParams.java new file mode 100644 index 0000000000..d2fdaad40d --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorTimerParams.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import java.time.Duration; + +/** + * Represents the timer set on an Actor, to be called once after due time and then every period. + */ +final class ActorTimerParams { + + /** + * Name of the method to be called for this timer. + */ + private String callback; + + /** + * Data to be sent in the timer. + */ + private String data; + + /** + * Due time for the timer's first trigger. + */ + private Duration dueTime; + + /** + * Period at which the timer will be triggered. + */ + private Duration period; + + /** + * Instantiates new params for Actor Timer. + * + * @param callback The name of the method to be called for this timer. + * @param data Information to be used by the callback method + * @param dueTime The time when timer is first due. + * @param period The periodic time when timer will be invoked. + */ + ActorTimerParams(String callback, + String data, + Duration dueTime, + Duration period) { + this.callback = callback; + this.data = data; + this.dueTime = dueTime; + this.period = period; + } + + /** + * Gets the name of the method for this Timer. + * + * @return The name of the method for this timer. + */ + public String getCallback() { + return this.callback; + } + + /** + * Gets the time when timer is first due. + * + * @return Time as Duration when timer is first due. + */ + public Duration getDueTime() { + return this.dueTime; + } + + /** + * Gets the periodic time when timer will be invoked. + * + * @return Periodic time as Duration when timer will be invoked. + */ + public Duration getPeriod() { + return this.period; + } + + /** + * Gets data to be used by the callback method, or null. + * + * @return Data to be used by the callback method, or null. + */ + public String getData() { + return this.data; + } + +} \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorType.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorType.java index b450b759b9..3c4403cc94 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorType.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorType.java @@ -14,10 +14,11 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ActorType { - /** - * Overrides Actor's name. - * @return Actor's name. - */ - String Name(); + /** + * Overrides Actor's name. + * + * @return Actor's name. + */ + String Name(); } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java index 2eeaf91c54..604cf43305 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java @@ -14,152 +14,152 @@ */ final class ActorTypeInformation { - /** - * Actor type's name. - */ - private final String name; - - /** - * Actor's implementation class. - */ - private final Class implementationClass; - - /** - * Actor's immediate interfaces. - */ - private final Collection> interfaces; - - /** - * Whether Actor type is abstract. - */ - private final boolean abstractClass; - - /** - * Whether Actor type is remindable. - */ - private final boolean remindable; - - /** - * Instantiates a new {@link ActorTypeInformation} - * - * @param name Actor type's name. - * @param implementationClass Actor's implementation class. - * @param interfaces Actor's immediate interfaces. - * @param abstractClass Whether Actor type is abstract. - * @param remindable Whether Actor type is remindable. - */ - private ActorTypeInformation(String name, - Class implementationClass, - Collection> interfaces, - boolean abstractClass, - boolean remindable) { - this.name = name; - this.implementationClass = implementationClass; - this.interfaces = interfaces; - this.abstractClass = abstractClass; - this.remindable = remindable; - } - - /** - * Returns the name of this ActorType. - * - * @return ActorType's name. - */ - public String getName() { - return this.name; - } - - /** - * Gets the type of the class implementing the actor. - * - * @return The {@link Class} of implementing the actor. - */ - public Class getImplementationClass() { - return this.implementationClass; - } - - /** - * Gets the actor interfaces which derive from {@link Actor} and implemented - * by actor class. - * - * @return Collection of actor interfaces. - */ - public Collection> getInterfaces() { - return Collections.unmodifiableCollection(this.interfaces); - } - - /** - * Gets a value indicating whether the class implementing actor is abstract. - * - * @return true if the class implementing actor is abstract, otherwise false. - */ - public boolean isAbstractClass() { - return this.abstractClass; - } - - /** - * Gets a value indicating whether the actor class implements - * {@link Remindable}. - * - * @return true if the actor class implements {@link Remindable}. - */ - public boolean isRemindable() { - return this.remindable; - } - - /** - * Creates the {@link ActorTypeInformation} from given Class. - * - * @param actorClass The type of class implementing the actor to create - * ActorTypeInformation for. - * @return ActorTypeInformation if successfully created for actorType or null. - */ - public static ActorTypeInformation tryCreate(Class actorClass) { - try { - return create(actorClass); - } catch (IllegalArgumentException e) { - return null; + /** + * Actor type's name. + */ + private final String name; + + /** + * Actor's implementation class. + */ + private final Class implementationClass; + + /** + * Actor's immediate interfaces. + */ + private final Collection> interfaces; + + /** + * Whether Actor type is abstract. + */ + private final boolean abstractClass; + + /** + * Whether Actor type is remindable. + */ + private final boolean remindable; + + /** + * Instantiates a new {@link ActorTypeInformation} + * + * @param name Actor type's name. + * @param implementationClass Actor's implementation class. + * @param interfaces Actor's immediate interfaces. + * @param abstractClass Whether Actor type is abstract. + * @param remindable Whether Actor type is remindable. + */ + private ActorTypeInformation(String name, + Class implementationClass, + Collection> interfaces, + boolean abstractClass, + boolean remindable) { + this.name = name; + this.implementationClass = implementationClass; + this.interfaces = interfaces; + this.abstractClass = abstractClass; + this.remindable = remindable; } - } - - /** - * Creates an {@link #ActorTypeInformation} from actorType. - * - * @param actorClass The class implementing the actor to create - * ActorTypeInformation for. - * @return {@link #ActorTypeInformation} created from actorType. - */ - public static ActorTypeInformation create(Class actorClass) { - if (!ActorTypeUtilities.isActor(actorClass)) { - throw new IllegalArgumentException( - String.format( - "The type '%s' is not an Actor. An actor type must derive from '%s'.", - actorClass == null ? "" : actorClass.getCanonicalName(), - Actor.class.getCanonicalName())); + + /** + * Returns the name of this ActorType. + * + * @return ActorType's name. + */ + public String getName() { + return this.name; } - // get all actor interfaces - Class[] actorInterfaces = actorClass.getInterfaces(); - - boolean isAbstract = Modifier.isAbstract(actorClass.getModifiers()); - // ensure that the if the actor type is not abstract it implements at least one actor interface - if ((actorInterfaces.length == 0) && !isAbstract) { - throw new IllegalArgumentException( - String.format( - "The actor type '%s' does not implement any actor interfaces or one of the " - + "interfaces implemented is not an actor interface. " - + "All interfaces(including its parent interface) implemented by actor type must " - + "be actor interface. An actor interface is the one that ultimately derives " - + "from '%s' type.", - actorClass == null ? "" : actorClass.getCanonicalName(), - Actor.class.getCanonicalName())); + /** + * Gets the type of the class implementing the actor. + * + * @return The {@link Class} of implementing the actor. + */ + public Class getImplementationClass() { + return this.implementationClass; } - boolean isRemindable = ActorTypeUtilities.isRemindableActor(actorClass); - ActorType actorTypeAnnotation = (ActorType) actorClass.getAnnotation(ActorType.class); - String typeName = actorTypeAnnotation != null ? actorTypeAnnotation.Name() : actorClass.getSimpleName(); + /** + * Gets the actor interfaces which derive from {@link Actor} and implemented + * by actor class. + * + * @return Collection of actor interfaces. + */ + public Collection> getInterfaces() { + return Collections.unmodifiableCollection(this.interfaces); + } - return new ActorTypeInformation(typeName, actorClass, Arrays.asList(actorInterfaces), isAbstract, isRemindable); - } + /** + * Gets a value indicating whether the class implementing actor is abstract. + * + * @return true if the class implementing actor is abstract, otherwise false. + */ + public boolean isAbstractClass() { + return this.abstractClass; + } + + /** + * Gets a value indicating whether the actor class implements + * {@link Remindable}. + * + * @return true if the actor class implements {@link Remindable}. + */ + public boolean isRemindable() { + return this.remindable; + } + + /** + * Creates the {@link ActorTypeInformation} from given Class. + * + * @param actorClass The type of class implementing the actor to create + * ActorTypeInformation for. + * @return ActorTypeInformation if successfully created for actorType or null. + */ + public static ActorTypeInformation tryCreate(Class actorClass) { + try { + return create(actorClass); + } catch (IllegalArgumentException e) { + return null; + } + } + + /** + * Creates an {@link #ActorTypeInformation} from actorType. + * + * @param actorClass The class implementing the actor to create + * ActorTypeInformation for. + * @return {@link #ActorTypeInformation} created from actorType. + */ + public static ActorTypeInformation create(Class actorClass) { + if (!ActorTypeUtilities.isActor(actorClass)) { + throw new IllegalArgumentException( + String.format( + "The type '%s' is not an Actor. An actor type must derive from '%s'.", + actorClass == null ? "" : actorClass.getCanonicalName(), + Actor.class.getCanonicalName())); + } + + // get all actor interfaces + Class[] actorInterfaces = actorClass.getInterfaces(); + + boolean isAbstract = Modifier.isAbstract(actorClass.getModifiers()); + // ensure that the if the actor type is not abstract it implements at least one actor interface + if ((actorInterfaces.length == 0) && !isAbstract) { + throw new IllegalArgumentException( + String.format( + "The actor type '%s' does not implement any actor interfaces or one of the " + + "interfaces implemented is not an actor interface. " + + "All interfaces(including its parent interface) implemented by actor type must " + + "be actor interface. An actor interface is the one that ultimately derives " + + "from '%s' type.", + actorClass == null ? "" : actorClass.getCanonicalName(), + Actor.class.getCanonicalName())); + } + + boolean isRemindable = ActorTypeUtilities.isRemindableActor(actorClass); + ActorType actorTypeAnnotation = (ActorType) actorClass.getAnnotation(ActorType.class); + String typeName = actorTypeAnnotation != null ? actorTypeAnnotation.Name() : actorClass.getSimpleName(); + + return new ActorTypeInformation(typeName, actorClass, Arrays.asList(actorInterfaces), isAbstract, isRemindable); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java index 67384508ce..0eba233ade 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java @@ -11,81 +11,81 @@ */ final class ActorTypeUtilities { - /** - * Gets all interfaces that extend Actor. - * - * @param clazz Actor class. - * @return Array of Actor interfaces. - */ - public static Class[] getActorInterfaces(Class clazz) { - if (clazz == null) { - return new Class[0]; + /** + * Gets all interfaces that extend Actor. + * + * @param clazz Actor class. + * @return Array of Actor interfaces. + */ + public static Class[] getActorInterfaces(Class clazz) { + if (clazz == null) { + return new Class[0]; + } + + return Arrays.stream(clazz.getInterfaces()) + .filter(t -> Actor.class.isAssignableFrom(t)) + .filter(t -> getNonActorParentClass(t) == null) + .toArray(Class[]::new); } - return Arrays.stream(clazz.getInterfaces()) - .filter(t -> Actor.class.isAssignableFrom(t)) - .filter(t -> getNonActorParentClass(t) == null) - .toArray(Class[]::new); - } + /** + * Determines if given class is an Actor interface. + * + * @param clazz Actor interface candidate. + * @return Whether this is an Actor interface. + */ + public static boolean isActorInterface(Class clazz) { + return (clazz != null) && clazz.isInterface() && (getNonActorParentClass(clazz) == null); + } - /** - * Determines if given class is an Actor interface. - * - * @param clazz Actor interface candidate. - * @return Whether this is an Actor interface. - */ - public static boolean isActorInterface(Class clazz) { - return (clazz != null) && clazz.isInterface() && (getNonActorParentClass(clazz) == null); - } + /** + * Determines whether this is an Actor class. + * + * @param clazz Actor class candidate. + * @return Whether this is an Actor class. + */ + public static boolean isActor(Class clazz) { + if (clazz == null) { + return false; + } - /** - * Determines whether this is an Actor class. - * - * @param clazz Actor class candidate. - * @return Whether this is an Actor class. - */ - public static boolean isActor(Class clazz) { - if (clazz == null) { - return false; + return AbstractActor.class.isAssignableFrom(clazz); } - return AbstractActor.class.isAssignableFrom(clazz); - } + /** + * Determines whether this is an remindable Actor. + * + * @param clazz Actor class. + * @return Whether this is an remindable Actor. + */ + public static boolean isRemindableActor(Class clazz) { + return (clazz != null) && isActor(clazz) && (Arrays.stream(clazz.getInterfaces()).filter(t -> t.equals(Remindable.class)).count() > 0); + } - /** - * Determines whether this is an remindable Actor. - * - * @param clazz Actor class. - * @return Whether this is an remindable Actor. - */ - public static boolean isRemindableActor(Class clazz) { - return (clazz != null) && isActor(clazz) && (Arrays.stream(clazz.getInterfaces()).filter(t -> t.equals(Remindable.class)).count() > 0); - } + /** + * Returns the parent class if it is not the {@link AbstractActor} parent + * class. + * + * @param clazz Actor class. + * @return Parent class or null if it is {@link AbstractActor}. + */ + public static Class getNonActorParentClass(Class clazz) { + if (clazz == null) { + return null; + } - /** - * Returns the parent class if it is not the {@link AbstractActor} parent - * class. - * - * @param clazz Actor class. - * @return Parent class or null if it is {@link AbstractActor}. - */ - public static Class getNonActorParentClass(Class clazz) { - if (clazz == null) { - return null; - } + Class[] items = Arrays.stream(clazz.getInterfaces()).filter(t -> !t.equals(Actor.class)).toArray(Class[]::new); + if (items.length == 0) { + return clazz; + } - Class[] items = Arrays.stream(clazz.getInterfaces()).filter(t -> !t.equals(Actor.class)).toArray(Class[]::new); - if (items.length == 0) { - return clazz; - } + for (Class c : items) { + Class nonActorParent = getNonActorParentClass(c); + if (nonActorParent != null) { + return nonActorParent; + } + } - for (Class c : items) { - Class nonActorParent = getNonActorParentClass(c); - if (nonActorParent != null) { - return nonActorParent; - } + return null; } - - return null; - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java index 5b270dcb24..2710b25bff 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java @@ -11,65 +11,65 @@ */ interface AppToDaprAsyncClient { - /** - * Gets a state from Dapr's Actor. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param keyName State name. - * @return Asynchronous result with current state value. - */ - Mono getState(String actorType, String actorId, String keyName); + /** + * Gets a state from Dapr's Actor. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param keyName State name. + * @return Asynchronous result with current state value. + */ + Mono getState(String actorType, String actorId, String keyName); - /** - * Saves state batch to Dapr. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param data State to be saved. - * @return Asynchronous void result. - */ - Mono saveStateTransactionally(String actorType, String actorId, String data); + /** + * Saves state batch to Dapr. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param data State to be saved. + * @return Asynchronous void result. + */ + Mono saveStateTransactionally(String actorType, String actorId, String data); - /** - * Register a reminder. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param reminderName Name of reminder to be registered. - * @param data JSON reminder data as per Dapr's spec. - * @return Asynchronous void result. - */ - Mono registerReminder(String actorType, String actorId, String reminderName, String data); + /** + * Register a reminder. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param reminderName Name of reminder to be registered. + * @param data JSON reminder data as per Dapr's spec. + * @return Asynchronous void result. + */ + Mono registerReminder(String actorType, String actorId, String reminderName, String data); - /** - * Unregisters a reminder. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param reminderName Name of reminder to be unregistered. - * @return Asynchronous void result. - */ - Mono unregisterReminder(String actorType, String actorId, String reminderName); + /** + * Unregisters a reminder. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param reminderName Name of reminder to be unregistered. + * @return Asynchronous void result. + */ + Mono unregisterReminder(String actorType, String actorId, String reminderName); - /** - * Registers a timer. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param timerName Name of timer to be registered. - * @param data JSON reminder data as per Dapr's spec. - * @return Asynchronous void result. - */ - Mono registerTimer(String actorType, String actorId, String timerName, String data); + /** + * Registers a timer. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param timerName Name of timer to be registered. + * @param data JSON reminder data as per Dapr's spec. + * @return Asynchronous void result. + */ + Mono registerTimer(String actorType, String actorId, String timerName, String data); - /** - * Unregisters a timer. - * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param timerName Name of timer to be unregistered. - * @return Asynchronous void result. - */ - Mono unregisterTimer(String actorType, String actorId, String timerName); + /** + * Unregisters a timer. + * + * @param actorType Type of actor. + * @param actorId Actor Identifier. + * @param timerName Name of timer to be unregistered. + * @return Asynchronous void result. + */ + Mono unregisterTimer(String actorType, String actorId, String timerName); } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprClientBuilder.java b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprClientBuilder.java index 982e1bc167..cfc2955bb3 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprClientBuilder.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprClientBuilder.java @@ -12,14 +12,14 @@ */ class AppToDaprClientBuilder extends AbstractClientBuilder { - /** - * Builds an async client. - * - * @return Builds an async client. - */ - public AppToDaprAsyncClient buildAsyncClient() { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - // TODO: Expose configurations for OkHttpClient or com.microsoft.rest.RestClient. - return new AppToDaprHttpAsyncClient(super.getPort(), builder.build()); - } + /** + * Builds an async client. + * + * @return Builds an async client. + */ + public AppToDaprAsyncClient buildAsyncClient() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + // TODO: Expose configurations for OkHttpClient or com.microsoft.rest.RestClient. + return new AppToDaprHttpAsyncClient(super.getPort(), builder.build()); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprHttpAsyncClient.java b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprHttpAsyncClient.java index 93419488f1..5611883db1 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprHttpAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprHttpAsyncClient.java @@ -4,16 +4,13 @@ */ package io.dapr.actors.runtime; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.dapr.actors.utils.ObjectSerializer; import io.dapr.client.AbstractDaprHttpClient; -import io.dapr.actors.Constants; import io.dapr.exceptions.DaprException; +import io.dapr.utils.Constants; +import io.dapr.utils.ObjectSerializer; import okhttp3.OkHttpClient; import reactor.core.publisher.Mono; -import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -23,189 +20,189 @@ //public class DaprHttpAsyncClient implements DaprAsyncClient { class AppToDaprHttpAsyncClient extends AbstractDaprHttpClient implements AppToDaprAsyncClient { - /** - * ObjectMapper to Serialize data - */ - private static final ObjectSerializer MAPPER = new ObjectSerializer(); - - private Map dataMap; - - - /** - * Creates a new instance of {@link AppToDaprHttpAsyncClient}. - * - * @param port Port for calling Dapr. (e.g. 3500) - * @param httpClient RestClient used for all API calls in this new instance. - */ - public AppToDaprHttpAsyncClient(int port, OkHttpClient httpClient) { - super(port, httpClient); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono getState(String actorType, String actorId, String keyName) { - String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); - return super.invokeAPI("GET", url, null); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono saveStateTransactionally(String actorType, String actorId, String data) { - String url = String.format(Constants.ACTOR_STATE_RELATIVE_URL_FORMAT, actorType, actorId); - return super.invokeAPIVoid("PUT", url, data); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono registerReminder(String actorType, String actorId, String reminderName, String data) { - String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); - return super.invokeAPIVoid("PUT", url, data); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono unregisterReminder(String actorType, String actorId, String reminderName) { - String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); - return super.invokeAPIVoid("DELETE", url, null); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono registerTimer(String actorType, String actorId, String timerName, String data) { - String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); - return super.invokeAPIVoid("PUT", url, data); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono unregisterTimer(String actorType, String actorId, String timerName) { - String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); - return super.invokeAPIVoid("DELETE", url, null); - } - - /** - * Creating publishEvent for Http Client - * - * @param topic HTTP method. - * @param data url as String. - * @param method JSON payload or null. - * @return Mono - */ - public Mono publishEvent(String topic, String data, String method) throws Exception { - - if (topic.isEmpty() || topic == null ) { - throw new DaprException("500" , "Topic cannot be null or empty."); - } + /** + * ObjectMapper to Serialize data + */ + private static final ObjectSerializer MAPPER = new ObjectSerializer(); + + private Map dataMap; + - if ( method.isEmpty() || method == null ) { - throw new DaprException("500", "Method cannot be null or empty."); + /** + * Creates a new instance of {@link AppToDaprHttpAsyncClient}. + * + * @param port Port for calling Dapr. (e.g. 3500) + * @param httpClient RestClient used for all API calls in this new instance. + */ + public AppToDaprHttpAsyncClient(int port, OkHttpClient httpClient) { + super(port, httpClient); } - String url = method.equals("POST") ? Constants.PUBLISH_PATH : Constants.PUBLISH_PATH + "/" + topic; + /** + * {@inheritDoc} + */ + @Override + public Mono getState(String actorType, String actorId, String keyName) { + String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); + return super.invokeAPI("GET", url, null); + } - dataMap = new HashMap(); - dataMap.put(topic,data); + /** + * {@inheritDoc} + */ + @Override + public Mono saveStateTransactionally(String actorType, String actorId, String data) { + String url = String.format(Constants.ACTOR_STATE_RELATIVE_URL_FORMAT, actorType, actorId); + return super.invokeAPIVoid("PUT", url, data); + } - String jsonResult = MAPPER.serialize(dataMap); + /** + * {@inheritDoc} + */ + @Override + public Mono registerReminder(String actorType, String actorId, String reminderName, String data) { + String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); + return super.invokeAPIVoid("PUT", url, data); + } - return super.invokeAPI(method, url, jsonResult); - } + /** + * {@inheritDoc} + */ + @Override + public Mono unregisterReminder(String actorType, String actorId, String reminderName) { + String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); + return super.invokeAPIVoid("DELETE", url, null); + } - /** - * Creating invokeBinding Method for Http Client - * - * @param name HTTP method. - * @param data url as String. - * @param method JSON payload or null. - * @return Mono - */ - public Mono invokeBinding(String name, String data, String method) throws Exception { + /** + * {@inheritDoc} + */ + @Override + public Mono registerTimer(String actorType, String actorId, String timerName, String data) { + String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); + return super.invokeAPIVoid("PUT", url, data); + } - if (name.isEmpty() || name == null) { - throw new DaprException("500", "Name cannot be null or empty."); + /** + * {@inheritDoc} + */ + @Override + public Mono unregisterTimer(String actorType, String actorId, String timerName) { + String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); + return super.invokeAPIVoid("DELETE", url, null); } - if (method.isEmpty() || method == null) { - throw new DaprException("500","Method cannot be null or empty."); + /** + * Creating publishEvent for Http Client + * + * @param topic HTTP method. + * @param data url as String. + * @param method JSON payload or null. + * @return Mono + */ + public Mono publishEvent(String topic, String data, String method) throws Exception { + + if (topic.isEmpty() || topic == null) { + throw new DaprException("500", "Topic cannot be null or empty."); + } + + if (method.isEmpty() || method == null) { + throw new DaprException("500", "Method cannot be null or empty."); + } + + String url = method.equals("POST") ? Constants.PUBLISH_PATH : Constants.PUBLISH_PATH + "/" + topic; + + dataMap = new HashMap(); + dataMap.put(topic, data); + + String jsonResult = MAPPER.serialize(dataMap); + + return super.invokeAPI(method, url, jsonResult); } - String url = method.equals("POST") ? Constants.BINDING_PATH : Constants.BINDING_PATH + "/" + name; + /** + * Creating invokeBinding Method for Http Client + * + * @param name HTTP method. + * @param data url as String. + * @param method JSON payload or null. + * @return Mono + */ + public Mono invokeBinding(String name, String data, String method) throws Exception { - dataMap = new HashMap(); - dataMap.put(name,data); + if (name.isEmpty() || name == null) { + throw new DaprException("500", "Name cannot be null or empty."); + } - String jsonResult = MAPPER.serialize(dataMap); + if (method.isEmpty() || method == null) { + throw new DaprException("500", "Method cannot be null or empty."); + } - return super.invokeAPI(method, url, jsonResult); - } + String url = method.equals("POST") ? Constants.BINDING_PATH : Constants.BINDING_PATH + "/" + name; - /** - * Creating invokeBinding Method for Http Client - * - * @param key HTTP method. - * @return Mono - */ - public Mono getState(String key) throws DaprException { + dataMap = new HashMap(); + dataMap.put(name, data); - if (key.isEmpty() || key == null) { - throw new DaprException("500", "Name cannot be null or empty."); + String jsonResult = MAPPER.serialize(dataMap); + + return super.invokeAPI(method, url, jsonResult); } - String url = Constants.STATE_PATH + "/" + key; + /** + * Creating invokeBinding Method for Http Client + * + * @param key HTTP method. + * @return Mono + */ + public Mono getState(String key) throws DaprException { - return super.invokeAPI("GET", url, null); - } + if (key.isEmpty() || key == null) { + throw new DaprException("500", "Name cannot be null or empty."); + } - /** - * Creating invokeBinding Method for Http Client - * - * @param key HTTP method. - * @param data HTTP method. - * @return Mono - */ - public Mono saveState(String key, String data) throws Exception { + String url = Constants.STATE_PATH + "/" + key; - if (key.isEmpty() || key == null) { - throw new DaprException("500", "Name cannot be null or empty."); + return super.invokeAPI("GET", url, null); } - String url = Constants.STATE_PATH; + /** + * Creating invokeBinding Method for Http Client + * + * @param key HTTP method. + * @param data HTTP method. + * @return Mono + */ + public Mono saveState(String key, String data) throws Exception { - dataMap = new HashMap(); - dataMap.put(key,data); + if (key.isEmpty() || key == null) { + throw new DaprException("500", "Name cannot be null or empty."); + } - String jsonResult = MAPPER.serialize(dataMap); + String url = Constants.STATE_PATH; - return super.invokeAPI("POST", url, jsonResult); - } + dataMap = new HashMap(); + dataMap.put(key, data); - /** - * Creating invokeBinding Method for Http Client - * - * @param key HTTP method. - * @return Mono - */ - public Mono deleteState(String key) throws DaprException { + String jsonResult = MAPPER.serialize(dataMap); - if (key.isEmpty() || key == null) { - throw new DaprException("500", "Name cannot be null or empty."); + return super.invokeAPI("POST", url, jsonResult); } - String url = Constants.STATE_PATH + "/" + key; + /** + * Creating invokeBinding Method for Http Client + * + * @param key HTTP method. + * @return Mono + */ + public Mono deleteState(String key) throws DaprException { + + if (key.isEmpty() || key == null) { + throw new DaprException("500", "Name cannot be null or empty."); + } - return super.invokeAPI("DELETE", url, null); - } + String url = Constants.STATE_PATH + "/" + key; + + return super.invokeAPI("DELETE", url, null); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java b/sdk/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java index 8a0136cbc8..34775a02ff 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java @@ -19,123 +19,123 @@ */ class DaprStateAsyncProvider { - /** - * Shared Json Factory as per Jackson's documentation, used only for this class. - */ - private static final JsonFactory JSON_FACTORY = new JsonFactory(); - - private final AppToDaprAsyncClient daprAsyncClient; - - private final ActorStateSerializer serializer; - - DaprStateAsyncProvider(AppToDaprAsyncClient daprAsyncClient, ActorStateSerializer serializer) { - this.daprAsyncClient = daprAsyncClient; - this.serializer = serializer; - } - - Mono load(String actorType, ActorId actorId, String stateName, Class clazz) { - Mono result = this.daprAsyncClient.getState(actorType, actorId.toString(), stateName); - - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> { - try { - return this.serializer.deserialize(s, clazz); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - - Mono contains(String actorType, ActorId actorId, String stateName) { - Mono result = this.daprAsyncClient.getState(actorType, actorId.toString(), stateName); - - return result.map(s -> { - return (s != null) && (s.length() > 0); - }); - } - - /** - * Saves state changes transactionally. - * [ - * { - * "operation": "upsert", - * "request": { - * "key": "key1", - * "value": "myData" - * } - * }, - * { - * "operation": "delete", - * "request": { - * "key": "key2" - * } - * } - * ] - * @param actorType Name of the actor being changed. - * @param actorId Identifier of the actor being changed. - * @param stateChanges Collection of changes to be performed transactionally. - * @return Void. - */ - Mono apply(String actorType, ActorId actorId, ActorStateChange... stateChanges) - { - if ((stateChanges == null) || stateChanges.length == 0) { - return Mono.empty(); + /** + * Shared Json Factory as per Jackson's documentation, used only for this class. + */ + private static final JsonFactory JSON_FACTORY = new JsonFactory(); + + private final AppToDaprAsyncClient daprAsyncClient; + + private final ActorStateSerializer serializer; + + DaprStateAsyncProvider(AppToDaprAsyncClient daprAsyncClient, ActorStateSerializer serializer) { + this.daprAsyncClient = daprAsyncClient; + this.serializer = serializer; } - int count = 0; - // Constructing the JSON via a stream API to avoid creating transient objects to be instantiated. - String payload = null; - try (Writer writer = new StringWriter()) { - JsonGenerator generator = JSON_FACTORY.createGenerator(writer); - // Start array - generator.writeStartArray(); - - for (ActorStateChange stateChange : stateChanges) { - if ((stateChange == null) || (stateChange.getChangeKind() == null)) { - continue; - } + Mono load(String actorType, ActorId actorId, String stateName, Class clazz) { + Mono result = this.daprAsyncClient.getState(actorType, actorId.toString(), stateName); + + return result + .filter(s -> (s != null) && (!s.isEmpty())) + .map(s -> { + try { + return this.serializer.deserialize(s, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } - String operationName = stateChange.getChangeKind().getDaprStateChangeOperation(); - if ((operationName == null) || (operationName.length() == 0)) { - continue; - } + Mono contains(String actorType, ActorId actorId, String stateName) { + Mono result = this.daprAsyncClient.getState(actorType, actorId.toString(), stateName); - count++; + return result.map(s -> { + return (s != null) && (s.length() > 0); + }); + } - // Start operation object. - generator.writeStartObject(); - generator.writeStringField("operation", operationName); + /** + * Saves state changes transactionally. + * [ + * { + * "operation": "upsert", + * "request": { + * "key": "key1", + * "value": "myData" + * } + * }, + * { + * "operation": "delete", + * "request": { + * "key": "key2" + * } + * } + * ] + * + * @param actorType Name of the actor being changed. + * @param actorId Identifier of the actor being changed. + * @param stateChanges Collection of changes to be performed transactionally. + * @return Void. + */ + Mono apply(String actorType, ActorId actorId, ActorStateChange... stateChanges) { + if ((stateChanges == null) || stateChanges.length == 0) { + return Mono.empty(); + } - // Start request object. - generator.writeObjectFieldStart("request"); - generator.writeStringField("key", stateChange.getStateName()); - if ((stateChange.getChangeKind() == ActorStateChangeKind.UPDATE) || (stateChange.getChangeKind() == ActorStateChangeKind.ADD)) { - generator.writeStringField("value", this.serializer.serialize(stateChange.getValue())); + int count = 0; + // Constructing the JSON via a stream API to avoid creating transient objects to be instantiated. + String payload = null; + try (Writer writer = new StringWriter()) { + JsonGenerator generator = JSON_FACTORY.createGenerator(writer); + // Start array + generator.writeStartArray(); + + for (ActorStateChange stateChange : stateChanges) { + if ((stateChange == null) || (stateChange.getChangeKind() == null)) { + continue; + } + + String operationName = stateChange.getChangeKind().getDaprStateChangeOperation(); + if ((operationName == null) || (operationName.length() == 0)) { + continue; + } + + count++; + + // Start operation object. + generator.writeStartObject(); + generator.writeStringField("operation", operationName); + + // Start request object. + generator.writeObjectFieldStart("request"); + generator.writeStringField("key", stateChange.getStateName()); + if ((stateChange.getChangeKind() == ActorStateChangeKind.UPDATE) || (stateChange.getChangeKind() == ActorStateChangeKind.ADD)) { + generator.writeStringField("value", this.serializer.serialize(stateChange.getValue())); + } + // End request object. + generator.writeEndObject(); + + // End operation object. + generator.writeEndObject(); + } + + // End array + generator.writeEndArray(); + + generator.close(); + writer.flush(); + payload = writer.toString(); + } catch (IOException e) { + e.printStackTrace(); + return Mono.error(e); } - // End request object. - generator.writeEndObject(); - - // End operation object. - generator.writeEndObject(); - } - - // End array - generator.writeEndArray(); - - generator.close(); - writer.flush(); - payload = writer.toString(); - } catch (IOException e) { - e.printStackTrace(); - return Mono.error(e); - } - if (count == 0) { - // No-op since there is no operation to be performed. - Mono.empty(); - } + if (count == 0) { + // No-op since there is no operation to be performed. + Mono.empty(); + } - return this.daprAsyncClient.saveStateTransactionally(actorType, actorId.toString(), payload); - } + return this.daprAsyncClient.saveStateTransactionally(actorType, actorId.toString(), payload); + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java b/sdk/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java index 40e140d331..b6ab5d797a 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java @@ -11,30 +11,31 @@ /** * Instantiates actors by calling their constructor with {@link ActorService} and {@link ActorId}. + * * @param Actor Type to be created. */ class DefaultActorFactory implements ActorFactory { - /** - * {@inheritDoc} - */ - @Override - public T createActor(ActorRuntimeContext actorRuntimeContext, ActorId actorId) { - try { - if (actorRuntimeContext == null) { - return null; - } + /** + * {@inheritDoc} + */ + @Override + public T createActor(ActorRuntimeContext actorRuntimeContext, ActorId actorId) { + try { + if (actorRuntimeContext == null) { + return null; + } - Constructor constructor = actorRuntimeContext - .getActorTypeInformation() - .getImplementationClass() - .getConstructor(ActorRuntimeContext.class, ActorId.class); - return constructor.newInstance(actorRuntimeContext, actorId); - } catch (Exception e) { - //TODO: Use ActorTrace. - e.printStackTrace(); + Constructor constructor = actorRuntimeContext + .getActorTypeInformation() + .getImplementationClass() + .getConstructor(ActorRuntimeContext.class, ActorId.class); + return constructor.newInstance(actorRuntimeContext, actorId); + } catch (Exception e) { + //TODO: Use ActorTrace. + e.printStackTrace(); + } + return null; } - return null; - } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/DurationUtils.java b/sdk/src/main/java/io/dapr/actors/runtime/DurationUtils.java index d12d2d0e8f..f09a19573c 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/DurationUtils.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/DurationUtils.java @@ -9,134 +9,134 @@ public class DurationUtils { - /** - * Converts time from the String format used by Dapr into a Duration. - * - * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). - * @return A Duration - */ - public static Duration ConvertDurationFromDaprFormat(String valueString) { - // Convert the format returned by the Dapr runtime into Duration - // An example of the format is: 4h15m50s60ms. It does not include days. - int hIndex = valueString.indexOf('h'); - int mIndex = valueString.indexOf('m'); - int sIndex = valueString.indexOf('s'); - int msIndex = valueString.indexOf("ms"); - - String hoursSpan = valueString.substring(0, hIndex); - - int hours = Integer.parseInt(hoursSpan); - int days = hours / 24; - hours = hours % 24; - - String minutesSpan = valueString.substring(hIndex + 1, mIndex); - int minutes = Integer.parseInt(minutesSpan); - - String secondsSpan = valueString.substring(mIndex + 1, sIndex); - int seconds = Integer.parseInt(secondsSpan); - - String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); - int milliseconds = Integer.parseInt(millisecondsSpan); - - return Duration.ZERO - .plusDays(days) - .plusHours(hours) - .plusMinutes(minutes) - .plusSeconds(seconds) - .plusMillis(milliseconds); - } - - /** - * Converts a Duration to the format used by the Dapr runtime. - * - * @param value Duration - * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) - */ - public static String ConvertDurationToDaprFormat(Duration value) { - String stringValue = ""; - - // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A - // negative "period" means fire once only. - if (value == Duration.ZERO || - (value.compareTo(Duration.ZERO) == 1)) { - long hours = getDaysPart(value) * 24 + getHoursPart(value); - - StringBuilder sb = new StringBuilder(); - - sb.append(hours); - sb.append("h"); - - sb.append(getMinutesPart((value))); - sb.append("m"); - - sb.append(getSecondsPart((value))); - sb.append("s"); - - sb.append(getMilliSecondsPart((value))); - sb.append("ms"); - - return sb.toString(); + /** + * Converts time from the String format used by Dapr into a Duration. + * + * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). + * @return A Duration + */ + public static Duration ConvertDurationFromDaprFormat(String valueString) { + // Convert the format returned by the Dapr runtime into Duration + // An example of the format is: 4h15m50s60ms. It does not include days. + int hIndex = valueString.indexOf('h'); + int mIndex = valueString.indexOf('m'); + int sIndex = valueString.indexOf('s'); + int msIndex = valueString.indexOf("ms"); + + String hoursSpan = valueString.substring(0, hIndex); + + int hours = Integer.parseInt(hoursSpan); + int days = hours / 24; + hours = hours % 24; + + String minutesSpan = valueString.substring(hIndex + 1, mIndex); + int minutes = Integer.parseInt(minutesSpan); + + String secondsSpan = valueString.substring(mIndex + 1, sIndex); + int seconds = Integer.parseInt(secondsSpan); + + String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); + int milliseconds = Integer.parseInt(millisecondsSpan); + + return Duration.ZERO + .plusDays(days) + .plusHours(hours) + .plusMinutes(minutes) + .plusSeconds(seconds) + .plusMillis(milliseconds); } - return stringValue; - } - - /** - * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. - * - * @param d Duration - * @return Number of days. - */ - static long getDaysPart(Duration d) { - long t = d.getSeconds() / 60 / 60 / 24; - return t; - } - - /** - * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. - * - * @param d The duration to parse - * @return the hour part of the duration - */ - static long getHoursPart(Duration d) { - long u = (d.getSeconds() / 60 / 60) % 24; - - return u; - } - - /** - * Helper to get the "minutes" part of the Duration. - * - * @param d The duration to parse - * @return the minutes part of the duration - */ - static long getMinutesPart(Duration d) { - long u = (d.getSeconds() / 60) % 60; - - return u; - } - - /** - * Helper to get the "seconds" part of the Duration. - * - * @param d The duration to parse - * @return the seconds part of the duration - */ - static long getSecondsPart(Duration d) { - long u = d.getSeconds() % 60; - - return u; - } - - /** - * Helper to get the "millis" part of the Duration. - * - * @param d The duration to parse - * @return the milliseconds part of the duration - */ - static long getMilliSecondsPart(Duration d) { - long u = d.toMillis() % 1000; - - return u; - } + /** + * Converts a Duration to the format used by the Dapr runtime. + * + * @param value Duration + * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) + */ + public static String ConvertDurationToDaprFormat(Duration value) { + String stringValue = ""; + + // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A + // negative "period" means fire once only. + if (value == Duration.ZERO || + (value.compareTo(Duration.ZERO) == 1)) { + long hours = getDaysPart(value) * 24 + getHoursPart(value); + + StringBuilder sb = new StringBuilder(); + + sb.append(hours); + sb.append("h"); + + sb.append(getMinutesPart((value))); + sb.append("m"); + + sb.append(getSecondsPart((value))); + sb.append("s"); + + sb.append(getMilliSecondsPart((value))); + sb.append("ms"); + + return sb.toString(); + } + + return stringValue; + } + + /** + * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. + * + * @param d Duration + * @return Number of days. + */ + static long getDaysPart(Duration d) { + long t = d.getSeconds() / 60 / 60 / 24; + return t; + } + + /** + * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. + * + * @param d The duration to parse + * @return the hour part of the duration + */ + static long getHoursPart(Duration d) { + long u = (d.getSeconds() / 60 / 60) % 24; + + return u; + } + + /** + * Helper to get the "minutes" part of the Duration. + * + * @param d The duration to parse + * @return the minutes part of the duration + */ + static long getMinutesPart(Duration d) { + long u = (d.getSeconds() / 60) % 60; + + return u; + } + + /** + * Helper to get the "seconds" part of the Duration. + * + * @param d The duration to parse + * @return the seconds part of the duration + */ + static long getSecondsPart(Duration d) { + long u = d.getSeconds() % 60; + + return u; + } + + /** + * Helper to get the "millis" part of the Duration. + * + * @param d The duration to parse + * @return the milliseconds part of the duration + */ + static long getMilliSecondsPart(Duration d) { + long u = d.toMillis() % 1000; + + return u; + } } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/Remindable.java b/sdk/src/main/java/io/dapr/actors/runtime/Remindable.java index 84843affef..768a51d41f 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/Remindable.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/Remindable.java @@ -13,24 +13,25 @@ */ public interface Remindable { - /** - * Gets the class for state object. - * @return Class for state object. - */ - Class getReminderStateType(); + /** + * Gets the class for state object. + * + * @return Class for state object. + */ + Class getStateType(); - /** - * The reminder call back invoked when an actor reminder is triggered. - * - * The state of this actor is saved by the actor runtime upon completion of the task returned by this method. - * If an error occurs while saving the state, then all state cached by this actor's {@link ActorStateManager} will - * be discarded and reloaded from previously saved state when the next actor method or reminder invocation occurs. - * - * @param reminderName The name of reminder provided during registration. - * @param state The user state provided during registration. - * @param dueTime The invocation due time provided during registration. - * @param period The invocation period provided during registration. - * @return A task that represents the asynchronous operation performed by this callback. - */ - Mono receiveReminder(String reminderName, T state, Duration dueTime, Duration period); + /** + * The reminder call back invoked when an actor reminder is triggered. + *

+ * The state of this actor is saved by the actor runtime upon completion of the task returned by this method. + * If an error occurs while saving the state, then all state cached by this actor's {@link ActorStateManager} will + * be discarded and reloaded from previously saved state when the next actor method or reminder invocation occurs. + * + * @param reminderName The name of reminder provided during registration. + * @param state The user state provided during registration. + * @param dueTime The invocation due time provided during registration. + * @param period The invocation period provided during registration. + * @return A task that represents the asynchronous operation performed by this callback. + */ + Mono receiveReminder(String reminderName, T state, Duration dueTime, Duration period); } diff --git a/sdk/src/main/java/io/dapr/actors/utils/ObjectSerializer.java b/sdk/src/main/java/io/dapr/actors/utils/ObjectSerializer.java deleted file mode 100644 index 251daae75c..0000000000 --- a/sdk/src/main/java/io/dapr/actors/utils/ObjectSerializer.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ -package io.dapr.actors.utils; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.io.IOException; - -/** - * Serializes and deserializes an object. - */ -public class ObjectSerializer { - - /** - * Shared Json Factory as per Jackson's documentation, used only for this class. - */ - protected static final JsonFactory JSON_FACTORY = new JsonFactory(); - - /** - * Shared Json serializer/deserializer as per Jackson's documentation. - */ - protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - /** - * Serializes a given state object into byte array. - * - * @param state State object to be serialized. - * @return Array of bytes[] with the serialized content. - * @throws IOException - */ - public String serialize(T state) throws IOException { - if (state == null) { - return null; - } - - if (state.getClass() == String.class) { - return state.toString(); - } - - if (isPrimitiveOrEquivalent(state.getClass())) { - return state.toString(); - } - - // Not string, not primitive, so it is a complex type: we use JSON for that. - return OBJECT_MAPPER.writeValueAsString(state); - } - - /** - * Deserializes the byte array into the original object. - * - * @param value String to be parsed. - * @param clazz Type of the object being deserialized. - * @param Generic type of the object being deserialized. - * @return Object of type T. - * @throws IOException - */ - public T deserialize(String value, Class clazz) throws IOException { - if (clazz == String.class) { - return (T) value; - } - - if (isPrimitiveOrEquivalent(clazz)) { - return parse(value, clazz); - } - - if (value == null) { - return (T) null; - } - - // Not string, not primitive, so it is a complex type: we use JSON for that. - return OBJECT_MAPPER.readValue(value, clazz); - } - - /** - * Checks if the class is a primitive or equivalent. - * @param clazz Class to be checked. - * @return True if primitive or equivalent. - */ - private static boolean isPrimitiveOrEquivalent(Class clazz) { - if (clazz == null) { - return false; - } - - return (clazz.isPrimitive() || - (clazz == Boolean.class) || - (clazz == Character.class) || - (clazz == Byte.class) || - (clazz == Short.class) || - (clazz == Integer.class) || - (clazz == Long.class) || - (clazz == Float.class) || - (clazz == Double.class) || - (clazz == Void.class)); - } - - /** - * Parses a given String to the corresponding object defined by class. - * @param value String to be parsed. - * @param clazz Class of the expected result type. - * @param Result type. - * @return Result as corresponding type. - */ - private static T parse(String value, Class clazz) { - if (value == null) { - if (boolean.class == clazz) return (T) Boolean.FALSE; - if (byte.class == clazz) return (T) Byte.valueOf((byte) 0); - if (short.class == clazz) return (T) Short.valueOf((short) 0); - if (int.class == clazz) return (T) Integer.valueOf(0); - if (long.class == clazz) return (T) Long.valueOf(0L); - if (float.class == clazz) return (T) Float.valueOf(0); - if (double.class == clazz) return (T) Double.valueOf(0); - - return null; - } - - if ((Boolean.class == clazz) || (boolean.class == clazz)) return (T) Boolean.valueOf(value); - if ((Byte.class == clazz) || (byte.class == clazz)) return (T) Byte.valueOf(value); - if ((Short.class == clazz) || (short.class == clazz)) return (T) Short.valueOf(value); - if ((Integer.class == clazz) || (int.class == clazz)) return (T) Integer.valueOf(value); - if ((Long.class == clazz) || (long.class == clazz)) return (T) Long.valueOf(value); - if ((Float.class == clazz) || (float.class == clazz)) return (T) Float.valueOf(value); - if ((Double.class == clazz) || (double.class == clazz)) return (T) Double.valueOf(value); - - return null; - } -} diff --git a/sdk/src/main/java/io/dapr/client/AbstractClientBuilder.java b/sdk/src/main/java/io/dapr/client/AbstractClientBuilder.java index 60ead0dbbe..e64eae2772 100644 --- a/sdk/src/main/java/io/dapr/client/AbstractClientBuilder.java +++ b/sdk/src/main/java/io/dapr/client/AbstractClientBuilder.java @@ -4,55 +4,56 @@ */ package io.dapr.client; -import io.dapr.actors.Constants; +import io.dapr.utils.Constants; /** * Base class for client builders */ public abstract class AbstractClientBuilder { - /** - * Default port for Dapr after checking environment variable. - */ - private int port = AbstractClientBuilder.GetEnvPortOrDefault(); - - /** - * Overrides the port. - * - * @param port New port. - * @return This instance. - */ - public AbstractClientBuilder withPort(int port) { - this.port = port; - return this; - } - - /** - * Returns configured port. - * @return - */ - protected int getPort() { - return this.port; - } - - /** - * Tries to get a valid port from environment variable or returns default. - * - * @return Port defined in env variable or default. - */ - private static int GetEnvPortOrDefault() { - String envPort = System.getenv(Constants.ENV_DAPR_HTTP_PORT); - if (envPort == null) { - return Constants.DEFAULT_PORT; + /** + * Default port for Dapr after checking environment variable. + */ + private int port = AbstractClientBuilder.GetEnvPortOrDefault(); + + /** + * Overrides the port. + * + * @param port New port. + * @return This instance. + */ + public AbstractClientBuilder withPort(int port) { + this.port = port; + return this; } - try { - return Integer.parseInt(envPort.trim()); - } catch (NumberFormatException e) { - e.printStackTrace(); + /** + * Returns configured port. + * + * @return Port to connect to Dapr. + */ + protected int getPort() { + return this.port; } - return Constants.DEFAULT_PORT; - } + /** + * Tries to get a valid port from environment variable or returns default. + * + * @return Port defined in env variable or default. + */ + private static int GetEnvPortOrDefault() { + String envPort = System.getenv(Constants.ENV_DAPR_HTTP_PORT); + if (envPort == null) { + return Constants.DEFAULT_PORT; + } + + try { + return Integer.parseInt(envPort.trim()); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + return Constants.DEFAULT_PORT; + } } diff --git a/sdk/src/main/java/io/dapr/client/AbstractDaprHttpClient.java b/sdk/src/main/java/io/dapr/client/AbstractDaprHttpClient.java index f3f34507d1..342905cff0 100644 --- a/sdk/src/main/java/io/dapr/client/AbstractDaprHttpClient.java +++ b/sdk/src/main/java/io/dapr/client/AbstractDaprHttpClient.java @@ -5,180 +5,146 @@ package io.dapr.client; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.net.URL; -import java.util.UUID; - -import io.dapr.actors.Constants; import io.dapr.exceptions.DaprError; import io.dapr.exceptions.DaprException; +import io.dapr.utils.Constants; import okhttp3.*; import reactor.core.publisher.Mono; +import java.io.IOException; +import java.net.URL; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + public abstract class AbstractDaprHttpClient { - /** - * Defines the standard application/json type for HTTP calls in Dapr. - */ - private static final MediaType MEDIA_TYPE_APPLICATION_JSON = MediaType.get("application/json; charset=utf-8"); - - /** - * Shared object representing an empty request body in JSON. - */ - private static final RequestBody REQUEST_BODY_EMPTY_JSON = RequestBody.create(MEDIA_TYPE_APPLICATION_JSON, ""); - - /** - * JSON Object Mapper. - */ - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - /** - * The base url used for form urls. This is typically "http://localhost:3500". - */ - private final String baseUrl; - - /** - * Http client used for all API calls. - */ - private final OkHttpClient httpClient; - - /** - * Creates a new instance of {@link AbstractDaprHttpClient}. - * - * @param port Port for calling Dapr. (e.g. 3500) - * @param httpClient RestClient used for all API calls in this new instance. - */ - public AbstractDaprHttpClient(int port, OkHttpClient httpClient) { - this.baseUrl = String.format("http://%s:%d/", Constants.DEFAULT_HOSTNAME, port);; - this.httpClient = httpClient; - } - - // common methods - /** - * Invokes an API asynchronously that returns Void. - * - * @param method HTTP method. - * @param urlString url as String. - * @param json JSON payload or null. - * @return Asynchronous Void - */ - protected final Mono invokeAPIVoid(String method, String urlString, String json) { - return this.invokeAPI(method, urlString, json).then(); - } - - /** - * Invokes an API asynchronously that returns a text payload. - * - * @param method HTTP method. - * @param urlString url as String. - * @param json JSON payload or null. - * @return Asynchronous text - */ - public final Mono invokeAPI(String method, String urlString, String json) throws RuntimeException { - - DaprHttpCallback cb = new DaprHttpCallback() { - - @Override - public void onFailure(Call call, Exception e) { - Mono.error(e); - } - - @Override - public void onSuccess(String response) { - Mono.just(response); - } - }; - try { - tryInvokeAPI(method, urlString, json, cb); - } catch (Exception e) { - throw new RuntimeException(e); - } - return Mono.empty(); - } - - /** - * Invokes an API asynchronously and returns a text payload. - * - * @param method HTTP method. - * @param urlString url as String. - * @param json JSON payload or null. - * @return text - */ - private final void tryInvokeAPI(String method, String urlString, String json, final DaprHttpCallback cb) throws IOException, DaprException { - String requestId = UUID.randomUUID().toString(); - RequestBody body = json != null ? RequestBody.create(MEDIA_TYPE_APPLICATION_JSON, json) : REQUEST_BODY_EMPTY_JSON; - - Request request = new Request.Builder() - .url(new URL(this.baseUrl + urlString)) - .method(method, body) - .addHeader(Constants.HEADER_DAPR_REQUEST_ID, requestId) - .build(); - - this.httpClient.newCall(request).enqueue(new Callback() { - - @Override - public void onFailure(Call call, IOException e) { - cb.onFailure(call, e); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - DaprError error = parseDaprError(response.body().string()); - response.close(); - if ((error != null) && (error.getErrorCode() != null) && (error.getMessage() != null)) { - throw new DaprException(error); - } - } else { - String respBodyString = responseBody.string(); - cb.onSuccess(respBodyString); - response.close(); - } - } - } - }); - - } - - /** - * Tries to parse an error from Dapr response body. - * - * @param json Response body from Dapr. - * @return DaprError or null if could not parse. - */ - protected static DaprError parseDaprError(String json) { - if (json == null) { - return null; + /** + * Defines the standard application/json type for HTTP calls in Dapr. + */ + private static final MediaType MEDIA_TYPE_APPLICATION_JSON = + MediaType.get("application/json; charset=utf-8"); + + /** + * Shared object representing an empty request body in JSON. + */ + private static final RequestBody REQUEST_BODY_EMPTY_JSON = + RequestBody.Companion.create("", MEDIA_TYPE_APPLICATION_JSON); + + /** + * JSON Object Mapper. + */ + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + /** + * The base url used for form urls. This is typically "http://localhost:3500". + */ + private final String baseUrl; + + /** + * Http client used for all API calls. + */ + private final OkHttpClient httpClient; + + /** + * Thread-pool for HTTP calls. + */ + private final ExecutorService pool; + + /** + * Creates a new instance of {@link AbstractDaprHttpClient}. + * + * @param port Port for calling Dapr. (e.g. 3500) + * @param threadPoolSize Number of threads for http calls. + * @param httpClient RestClient used for all API calls in this new instance. + */ + public AbstractDaprHttpClient(int port, int threadPoolSize, OkHttpClient httpClient) { + this.baseUrl = String.format("http://%s:%d/", Constants.DEFAULT_HOSTNAME, port); + this.httpClient = httpClient; + this.pool = Executors.newFixedThreadPool(threadPoolSize); } - try { - return OBJECT_MAPPER.readValue(json, DaprError.class); - } catch (IOException e) { - e.printStackTrace(); - return null; + /** + * Creates a new instance of {@link AbstractDaprHttpClient}. + * + * @param port Port for calling Dapr. (e.g. 3500) + * @param httpClient RestClient used for all API calls in this new instance. + */ + public AbstractDaprHttpClient(int port, OkHttpClient httpClient) { + this(port, 1, httpClient); } - } - public interface DaprHttpCallback { + /** + * Invokes an API asynchronously that returns Void. + * + * @param method HTTP method. + * @param urlString url as String. + * @param json JSON payload or null. + * @return Asynchronous Void + */ + protected final Mono invokeAPIVoid(String method, String urlString, String json) { + return this.invokeAPI(method, urlString, json).then(); + } /** - * Called when the server response was not 2xx or when an exception was - * thrown in the process + * Invokes an API asynchronously that returns a text payload. * - * @param call - in case of server error (4xx, 5xx) this contains the server - * response in case of IO exception this is null - * @param e - contains the exception. in case of server error (4xx, 5xx) - * this is null + * @param method HTTP method. + * @param urlString url as String. + * @param json JSON payload or null. + * @return Asynchronous text */ - public void onFailure(Call call, Exception e); + public final Mono invokeAPI(String method, String urlString, String json) { + CompletableFuture future = CompletableFuture.supplyAsync( + () -> { + try { + String requestId = UUID.randomUUID().toString(); + RequestBody body = + json != null ? RequestBody.Companion.create(json, MEDIA_TYPE_APPLICATION_JSON) : REQUEST_BODY_EMPTY_JSON; + + Request request = new Request.Builder() + .url(new URL(this.baseUrl + urlString)) + .method(method, body) + .addHeader(Constants.HEADER_DAPR_REQUEST_ID, requestId) + .build(); + + try (Response response = this.httpClient.newCall(request).execute()) { + if (!response.isSuccessful()) { + DaprError error = parseDaprError(response.body().string()); + if ((error != null) && (error.getErrorCode() != null) && (error.getMessage() != null)) { + throw new RuntimeException(new DaprException(error)); + } + + throw new RuntimeException("Unknown error."); + } + String result = response.body().string(); + return result == null ? "" : result; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + }, this.pool); + + return Mono.fromFuture(future); + } /** - * Contains the server response + * Tries to parse an error from Dapr response body. * - * @param response Success response. + * @param json Response body from Dapr. + * @return DaprError or null if could not parse. */ - public void onSuccess(String response); - } + private static DaprError parseDaprError(String json) { + if (json == null) { + return null; + } + + try { + return OBJECT_MAPPER.readValue(json, DaprError.class); + } catch (IOException e) { + throw new RuntimeException("Unknown error: could not parse error json."); + } + } } diff --git a/sdk/src/main/java/io/dapr/client/DaprClient.java b/sdk/src/main/java/io/dapr/client/DaprClient.java index aedb8364f2..0414259c34 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprClient.java @@ -8,59 +8,66 @@ /** * Generic Client Adapter to be used regardless of the GRPC or the HTTP Client implementation required. + * * @see io.dapr.client.DaprClientBuilder for information on how to make instance for this interface. */ public interface DaprClient { - /** - * Publish an event. - * @param event the event to be published - * @param The type of event to be publishded. - * @return a Mono plan of type Void - */ - Mono publishEvent(T event); + /** + * Publish an event. + * + * @param event the event to be published + * @param The type of event to be publishded. + * @return a Mono plan of type Void + */ + Mono publishEvent(T event); - /** - * Invoke a service - * @param request The request to be sent to invoke the service - * @param clazz the Type needed as return for the call - * @param the Type of the return - * @param The Type of the request. - * @return A Mono Plan of type clazz - */ - Mono invokeService(K request, Class clazz); + /** + * Invoke a service + * + * @param request The request to be sent to invoke the service + * @param clazz the Type needed as return for the call + * @param the Type of the return + * @param The Type of the request. + * @return A Mono Plan of type clazz + */ + Mono invokeService(K request, Class clazz); - /** - * Creating a Binding - * @param request the request needed for the binding - * @param The type of the request. - * @return a Mono plan of type Void - */ - Mono invokeBinding(T request); + /** + * Creating a Binding + * + * @param request the request needed for the binding + * @param The type of the request. + * @return a Mono plan of type Void + */ + Mono invokeBinding(T request); - /** - * Retrieve a State based on their key. - * @param key The key of the State to be retrieved - * @param clazz the Type of State needed as return. - * @param the Type of the return - * @param The Type of the key of the State - * @return A Mono Plan for the requested State - */ - Mono getState(K key, Class clazz); + /** + * Retrieve a State based on their key. + * + * @param key The key of the State to be retrieved + * @param clazz the Type of State needed as return. + * @param the Type of the return + * @param The Type of the key of the State + * @return A Mono Plan for the requested State + */ + Mono getState(K key, Class clazz); - /** - * Save/Update a State. - * @param state the State to be saved - * @param the Type of the State - * @return a Mono plan of type Void - */ - Mono saveState(T state); + /** + * Save/Update a State. + * + * @param state the State to be saved + * @param the Type of the State + * @return a Mono plan of type Void + */ + Mono saveState(T state); - /** - * Delete a state - * @param key The key of the State to be removed - * @param The Type of the key of the State - * @return a Mono plan of type Void - */ - Mono deleteState(T key); + /** + * Delete a state + * + * @param key The key of the State to be removed + * @param The Type of the key of the State + * @return a Mono plan of type Void + */ + Mono deleteState(T key); } diff --git a/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java b/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java index 6d3000e8b6..55eb24aaa0 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java @@ -11,94 +11,100 @@ /** * A builder for the DaprClient, * Only 2 type of clients are supported at the moment, HTTP and GRPC. - * */ public class DaprClientBuilder { - /** - * The type of client supported. - */ - public static enum DaprClientTypeEnum { - GRPC, - HTTP; - } + /** + * The type of client supported. + */ + public enum DaprClientTypeEnum { + GRPC, + HTTP; + } - /** - * An indicator of the client to be build by the instance of the builder. - */ - private DaprClientTypeEnum clientType; - /** - * The host to be used by the client to communicate. - */ - private String host; - /** - * The port to be used by the client to communicate - */ - private Integer port; + /** + * An indicator of the client to be build by the instance of the builder. + */ + private DaprClientTypeEnum clientType; + /** + * The host to be used by the client to communicate. + */ + private String host; - /** - * Creates an instance of the builder setting the type of client to be creted - * @param clientType - */ - public DaprClientBuilder(DaprClientTypeEnum clientType) { - this.clientType = clientType; - } + /** + * The port to be used by the client to communicate + */ + private Integer port; - /** - * Sets the host to be used by the client - * @param host - * @return itself - */ - public DaprClientBuilder host(String host) { - this.host = host; - return this; - } + /** + * Creates an instance of the builder setting the type of client to be creted + * + * @param clientType Determines if clients need to be over Http or GRPC. + */ + public DaprClientBuilder(DaprClientTypeEnum clientType) { + this.clientType = clientType; + } - /** - * Sets the port to be used by the client - * @param port - * @return itself - */ - public DaprClientBuilder port(Integer port) { - this.port = port; - return this; - } + /** + * Sets the host to be used by the client + * + * @param host Host to connect to Dapr. + * @return itself + */ + public DaprClientBuilder withHost(String host) { + this.host = host; + return this; + } - /** - * Build an instance of the Client based on the provided setup. - * @return an instance of the setup Client - * @throws java.lang.IllegalStateException if any required field is missing - */ - public DaprClient build() { - if (DaprClientTypeEnum.GRPC.equals(this.clientType)) { - return buildDaprClientGrpc(); - } else if (DaprClientTypeEnum.HTTP.equals(this.clientType)) { - return buildDaprClientHttp(); + /** + * Sets the port to be used by the client + * + * @param port Port to connect to. + * @return itself + */ + public DaprClientBuilder withPort(Integer port) { + this.port = port; + return this; } - throw new IllegalStateException("Unsupported client type."); - } - /** - * Creates an instance of the GPRC Client. - * @return the GRPC Client. - * @throws java.lang.IllegalStateException if either host is missing or if port is missing or a negative number. - */ - private DaprClient buildDaprClientGrpc() { - if (null == this.host || "".equals(this.host.trim())) { - throw new IllegalStateException("Host must is required."); + /** + * Build an instance of the Client based on the provided setup. + * + * @return an instance of the setup Client + * @throws java.lang.IllegalStateException if any required field is missing + */ + public DaprClient build() { + if (DaprClientTypeEnum.GRPC.equals(this.clientType)) { + return buildDaprClientGrpc(); + } else if (DaprClientTypeEnum.HTTP.equals(this.clientType)) { + return buildDaprClientHttp(); + } + throw new IllegalStateException("Unsupported client type."); } - if (null == port || port <= 0) { - throw new IllegalStateException("Invalid port."); + + /** + * Creates an instance of the GPRC Client. + * + * @return the GRPC Client. + * @throws java.lang.IllegalStateException if either host is missing or if port is missing or a negative number. + */ + private DaprClient buildDaprClientGrpc() { + if (null == this.host || "".equals(this.host.trim())) { + throw new IllegalStateException("Host must is required."); + } + if (null == port || port <= 0) { + throw new IllegalStateException("Invalid port."); + } + ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); + return new DaprClientGrpcAdapter(DaprGrpc.newFutureStub(channel)); } - ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); - return new DaprClientGrpcAdapter(DaprGrpc.newFutureStub(channel)); - } - /** - * Creates and instance of the HTTP CLient. - * @return - */ - private DaprClient buildDaprClientHttp() { - throw new UnsupportedOperationException("Not implemented yet."); - } + /** + * Creates and instance of the HTTP CLient. + * + * @return + */ + private DaprClient buildDaprClientHttp() { + throw new UnsupportedOperationException("Not implemented yet."); + } } diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index 7fdd364ccf..ff4ca17be5 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -5,173 +5,174 @@ package io.dapr.client; import com.google.common.util.concurrent.ListenableFuture; -import com.google.protobuf.*; +import com.google.protobuf.Empty; import io.dapr.DaprGrpc; import io.dapr.DaprProtos; import io.dapr.utils.ObjectSerializer; -import io.grpc.ManagedChannel; -import io.grpc.ManagedChannelBuilder; import reactor.core.publisher.Mono; /** * An adapter for the GRPC Client. + * * @see io.dapr.DaprGrpc * @see io.dapr.client.DaprClient */ class DaprClientGrpcAdapter implements DaprClient { - /** - * The GRPC client to be used - * @see io.dapr.DaprGrpc.DaprFutureStub - */ - private DaprGrpc.DaprFutureStub client; - /** - * A utitlity class for serialize and deserialize the messages sent and retrived by the client. - */ - private ObjectSerializer objectSerializer; + /** + * The GRPC client to be used + * + * @see io.dapr.DaprGrpc.DaprFutureStub + */ + private DaprGrpc.DaprFutureStub client; + /** + * A utitlity class for serialize and deserialize the messages sent and retrived by the client. + */ + private ObjectSerializer objectSerializer; - /** - * Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder - * @param futureClient - * @see io.dapr.client.DaprClientBuilder - */ - DaprClientGrpcAdapter(DaprGrpc.DaprFutureStub futureClient) { - client = futureClient; - objectSerializer = new ObjectSerializer(); - } + /** + * Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder + * + * @param futureClient + * @see io.dapr.client.DaprClientBuilder + */ + DaprClientGrpcAdapter(DaprGrpc.DaprFutureStub futureClient) { + client = futureClient; + objectSerializer = new ObjectSerializer(); + } - /** - * {@inheritDoc} - */ - @Override - public Mono publishEvent(T event) { - try { - String serializedEvent = objectSerializer.serialize(event); - DaprProtos.PublishEventEnvelope envelope = DaprProtos.PublishEventEnvelope.parseFrom(serializedEvent.getBytes()); - ListenableFuture futureEmpty = client.publishEvent(envelope); - return Mono.just(futureEmpty).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono publishEvent(T event) { try { - f.get(); + String serializedEvent = objectSerializer.serialize(event); + DaprProtos.PublishEventEnvelope envelope = DaprProtos.PublishEventEnvelope.parseFrom(serializedEvent.getBytes()); + ListenableFuture futureEmpty = client.publishEvent(envelope); + return Mono.just(futureEmpty).flatMap(f -> { + try { + f.get(); + } catch (Exception ex) { + return Mono.error(ex); + } + return Mono.empty(); + }); } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - return Mono.empty(); - }); - } catch (Exception ex) { - return Mono.error(ex); } - } - /** - * {@inheritDoc} - */ - @Override - public Mono invokeService(K request, Class clazz) { - try { - String serializedRequest = objectSerializer.serialize(request); - DaprProtos.InvokeServiceEnvelope envelope = - DaprProtos.InvokeServiceEnvelope.parseFrom(serializedRequest.getBytes()); - ListenableFuture futureResponse = - client.invokeService(envelope); - return Mono.just(futureResponse).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono invokeService(K request, Class clazz) { try { - return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toStringUtf8(), clazz)); + String serializedRequest = objectSerializer.serialize(request); + DaprProtos.InvokeServiceEnvelope envelope = + DaprProtos.InvokeServiceEnvelope.parseFrom(serializedRequest.getBytes()); + ListenableFuture futureResponse = + client.invokeService(envelope); + return Mono.just(futureResponse).flatMap(f -> { + try { + return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toStringUtf8(), clazz)); + } catch (Exception ex) { + return Mono.error(ex); + } + }); + } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - }); - - } catch (Exception ex) { - return Mono.error(ex); } - } - /** - * {@inheritDoc} - */ - @Override - public Mono invokeBinding(T request) { - try { - String serializedRequest = objectSerializer.serialize(request); - DaprProtos.InvokeBindingEnvelope envelope = - DaprProtos.InvokeBindingEnvelope.parseFrom(serializedRequest.getBytes()); - ListenableFuture futureEmpty = client.invokeBinding(envelope); - return Mono.just(futureEmpty).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono invokeBinding(T request) { try { - f.get(); + String serializedRequest = objectSerializer.serialize(request); + DaprProtos.InvokeBindingEnvelope envelope = + DaprProtos.InvokeBindingEnvelope.parseFrom(serializedRequest.getBytes()); + ListenableFuture futureEmpty = client.invokeBinding(envelope); + return Mono.just(futureEmpty).flatMap(f -> { + try { + f.get(); + } catch (Exception ex) { + return Mono.error(ex); + } + return Mono.empty(); + }); } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - return Mono.empty(); - }); - } catch (Exception ex) { - return Mono.error(ex); } - } - /** - * {@inheritDoc} - */ - @Override - public Mono getState(K key, Class clazz) { - try { - String serializedRequest = objectSerializer.serialize(key); - DaprProtos.GetStateEnvelope envelope = DaprProtos.GetStateEnvelope.parseFrom(serializedRequest.getBytes()); - ListenableFuture futureResponse = client.getState(envelope); - return Mono.just(futureResponse).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono getState(K key, Class clazz) { try { - return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toStringUtf8(), clazz)); + String serializedRequest = objectSerializer.serialize(key); + DaprProtos.GetStateEnvelope envelope = DaprProtos.GetStateEnvelope.parseFrom(serializedRequest.getBytes()); + ListenableFuture futureResponse = client.getState(envelope); + return Mono.just(futureResponse).flatMap(f -> { + try { + return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toStringUtf8(), clazz)); + } catch (Exception ex) { + return Mono.error(ex); + } + }); } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - }); - } catch (Exception ex) { - return Mono.error(ex); } - } - /** - * {@inheritDoc} - */ - @Override - public Mono saveState(T state) { - try { - String serializedRequest = objectSerializer.serialize(state); - DaprProtos.SaveStateEnvelope envelope = DaprProtos.SaveStateEnvelope.parseFrom(serializedRequest.getBytes()); - ListenableFuture futureEmpty = client.saveState(envelope); - return Mono.just(futureEmpty).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono saveState(T state) { try { - f.get(); + String serializedRequest = objectSerializer.serialize(state); + DaprProtos.SaveStateEnvelope envelope = DaprProtos.SaveStateEnvelope.parseFrom(serializedRequest.getBytes()); + ListenableFuture futureEmpty = client.saveState(envelope); + return Mono.just(futureEmpty).flatMap(f -> { + try { + f.get(); + } catch (Exception ex) { + return Mono.error(ex); + } + return Mono.empty(); + }); } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - return Mono.empty(); - }); - } catch (Exception ex) { - return Mono.error(ex); } - } - /** - * {@inheritDoc} - */ - @Override - public Mono deleteState(T key) { - try { - String serializedRequest = objectSerializer.serialize(key); - DaprProtos.DeleteStateEnvelope envelope = DaprProtos.DeleteStateEnvelope.parseFrom(serializedRequest.getBytes()); - ListenableFuture futureEmpty = client.deleteState(envelope); - return Mono.just(futureEmpty).flatMap(f -> { + /** + * {@inheritDoc} + */ + @Override + public Mono deleteState(T key) { try { - f.get(); + String serializedRequest = objectSerializer.serialize(key); + DaprProtos.DeleteStateEnvelope envelope = DaprProtos.DeleteStateEnvelope.parseFrom(serializedRequest.getBytes()); + ListenableFuture futureEmpty = client.deleteState(envelope); + return Mono.just(futureEmpty).flatMap(f -> { + try { + f.get(); + } catch (Exception ex) { + return Mono.error(ex); + } + return Mono.empty(); + }); } catch (Exception ex) { - return Mono.error(ex); + return Mono.error(ex); } - return Mono.empty(); - }); - } catch (Exception ex) { - return Mono.error(ex); } - } } \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/exceptions/DaprError.java b/sdk/src/main/java/io/dapr/exceptions/DaprError.java index e0d716a614..24dd4dc66e 100644 --- a/sdk/src/main/java/io/dapr/exceptions/DaprError.java +++ b/sdk/src/main/java/io/dapr/exceptions/DaprError.java @@ -9,54 +9,54 @@ */ public class DaprError { - /** - * Error code. - */ - private String errorCode; - - /** - * Error Message. - */ - private String message; - - /** - * Gets the error code. - * - * @return Error code. - */ - public String getErrorCode() { - return errorCode; - } - - /** - * Sets the error code. - * - * @param errorCode Error code. - * @return This instance. - */ - public DaprError setErrorCode(String errorCode) { - this.errorCode = errorCode; - return this; - } - - /** - * Gets the error message. - * - * @return Error message. - */ - public String getMessage() { - return message; - } - - /** - * Sets the error message. - * - * @param message Error message. - * @return This instance. - */ - public DaprError setMessage(String message) { - this.message = message; - return this; - } + /** + * Error code. + */ + private String errorCode; + + /** + * Error Message. + */ + private String message; + + /** + * Gets the error code. + * + * @return Error code. + */ + public String getErrorCode() { + return errorCode; + } + + /** + * Sets the error code. + * + * @param errorCode Error code. + * @return This instance. + */ + public DaprError setErrorCode(String errorCode) { + this.errorCode = errorCode; + return this; + } + + /** + * Gets the error message. + * + * @return Error message. + */ + public String getMessage() { + return message; + } + + /** + * Sets the error message. + * + * @param message Error message. + * @return This instance. + */ + public DaprError setMessage(String message) { + this.message = message; + return this; + } } diff --git a/sdk/src/main/java/io/dapr/exceptions/DaprException.java b/sdk/src/main/java/io/dapr/exceptions/DaprException.java index 2f758969d1..51149dbe02 100644 --- a/sdk/src/main/java/io/dapr/exceptions/DaprException.java +++ b/sdk/src/main/java/io/dapr/exceptions/DaprException.java @@ -11,37 +11,37 @@ */ public class DaprException extends IOException { - /** - * Dapr's error code for this exception. - */ - private String errorCode; + /** + * Dapr's error code for this exception. + */ + private String errorCode; - /** - * New exception from a server-side generated error code and message. - * - * @param daprError Server-side error. - */ - public DaprException(DaprError daprError) { - this(daprError.getErrorCode(), daprError.getMessage()); - } + /** + * New exception from a server-side generated error code and message. + * + * @param daprError Server-side error. + */ + public DaprException(DaprError daprError) { + this(daprError.getErrorCode(), daprError.getMessage()); + } - /** - * New Exception from a client-side generated error code and message. - * - * @param errorCode Client-side error code. - * @param message Client-side error message. - */ - public DaprException(String errorCode, String message) { - super(String.format("%s: %s", errorCode, message)); - this.errorCode = errorCode; - } + /** + * New Exception from a client-side generated error code and message. + * + * @param errorCode Client-side error code. + * @param message Client-side error message. + */ + public DaprException(String errorCode, String message) { + super(String.format("%s: %s", errorCode, message)); + this.errorCode = errorCode; + } - /** - * Returns the exception's error code. - * - * @return Error code. - */ - public String getErrorCode() { - return this.errorCode; - } + /** + * Returns the exception's error code. + * + * @return Error code. + */ + public String getErrorCode() { + return this.errorCode; + } } diff --git a/sdk/src/main/java/io/dapr/utils/Constants.java b/sdk/src/main/java/io/dapr/utils/Constants.java new file mode 100644 index 0000000000..8d87b3ace9 --- /dev/null +++ b/sdk/src/main/java/io/dapr/utils/Constants.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.utils; + +/** + * Useful constants for the Dapr's Actor SDK. + */ +public final class Constants { + + /** + * Dapr API used in this client. + */ + public static final String API_VERSION = "v1.0"; + + /** + * Dapr's default hostname. + */ + public static final String DEFAULT_HOSTNAME = "localhost"; + + /** + * Dapr's default port. + */ + public static final int DEFAULT_PORT = 3500; + + /** + * Environment variable used to set Dapr's port. + */ + public static final String ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; + + /** + * Header used for request id in Dapr. + */ + public static final String HEADER_DAPR_REQUEST_ID = "X-DaprRequestId"; + + /** + * Base URL for Dapr Actor APIs. + */ + private static final String ACTORS_BASE_URL = API_VERSION + "/" + "actors"; + + /** + * String format for Actors state management relative url. + */ + public static final String ACTOR_STATE_KEY_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/state/%s"; + + /** + * String format for Actors state management relative url. + */ + public static final String ACTOR_STATE_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/state"; + + /** + * String format for Actors method invocation relative url. + */ + public static final String ACTOR_METHOD_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/method/%s"; + + /** + * String format for Actors reminder registration relative url.. + */ + public static final String ACTOR_REMINDER_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/reminders/%s"; + + /** + * String format for Actors timer registration relative url.. + */ + public static final String ACTOR_TIMER_RELATIVE_URL_FORMAT = ACTORS_BASE_URL + "/%s/%s/timers/%s"; + + /** + * Invoke Publish Path + */ + public static final String PUBLISH_PATH = API_VERSION + "/publish"; + + /** + * Invoke Binding Path + */ + public static final String BINDING_PATH = API_VERSION + "/binding"; + + /** + * State Path + */ + public static final String STATE_PATH = API_VERSION + "/state"; +} diff --git a/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java b/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java index 3b398ee5db..13f50d3ac2 100644 --- a/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java +++ b/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java @@ -4,6 +4,7 @@ */ package io.dapr.utils; +import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; @@ -13,107 +14,161 @@ */ public class ObjectSerializer { - /** - * Shared Json serializer/deserializer as per Jackson's documentation. - */ - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - /** - * Serializes a given object object into byte array. - * - * @param object object to be serialized. - * @return Array of bytes[] with the serialized content. - * @throws IOException - */ - public String serialize(T object) throws IOException { - if (object == null) { - return null; + /** + * Shared Json Factory as per Jackson's documentation, used only for this class. + */ + protected static final JsonFactory JSON_FACTORY = new JsonFactory(); + + /** + * Shared Json serializer/deserializer as per Jackson's documentation. + */ + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + /** + * Serializes a given state object into byte array. + * + * @param state State object to be serialized. + * @param Type of the state object. + * @return Array of bytes[] with the serialized content. + * @throws IOException In case state cannot be serialized. + */ + public String serialize(T state) throws IOException { + if (state == null) { + return null; + } + + if (state.getClass() == String.class) { + return state.toString(); + } + + if (isPrimitiveOrEquivalent(state.getClass())) { + return state.toString(); + } + + // Not string, not primitive, so it is a complex type: we use JSON for that. + return OBJECT_MAPPER.writeValueAsString(state); } - if (object.getClass() == String.class) { - return object.toString(); + /** + * Deserializes the byte array into the original object. + * + * @param value Content to be parsed. + * @param clazz Type of the object being deserialized. + * @param Generic type of the object being deserialized. + * @return Object of type T. + * @throws IOException In case value cannot be deserialized. + */ + public T deserialize(Object value, Class clazz) throws IOException { + if (clazz == String.class) { + return (T) value; + } + + if (isPrimitiveOrEquivalent(clazz)) { + return parse(value, clazz); + } + + if (value == null) { + return (T) null; + } + + // Not string, not primitive, so it is a complex type: we use JSON for that. + if (value instanceof byte[]) { + return OBJECT_MAPPER.readValue((byte[]) value, clazz); + } + + return OBJECT_MAPPER.readValue(value.toString(), clazz); } - if (isPrimitiveOrEquivalent(object.getClass())) { - return object.toString(); + /** + * Checks if the class is a primitive or equivalent. + * + * @param clazz Class to be checked. + * @return True if primitive or equivalent. + */ + private static boolean isPrimitiveOrEquivalent(Class clazz) { + if (clazz == null) { + return false; + } + + return (clazz.isPrimitive() || + (clazz == Boolean.class) || + (clazz == Character.class) || + (clazz == Byte.class) || + (clazz == Short.class) || + (clazz == Integer.class) || + (clazz == Long.class) || + (clazz == Float.class) || + (clazz == Double.class) || + (clazz == Void.class)); } - // Not string, not primitive, so it is a complex type: we use JSON for that. - return OBJECT_MAPPER.writeValueAsString(object); - } - - /** - * Deserializes the byte array into the original object. - * - * @param value String to be parsed. - * @param clazz Type of the object being deserialized. - * @param Generic type of the object being deserialized. - * @return Object of type T. - * @throws IOException - */ - public T deserialize(String value, Class clazz) throws IOException { - if (clazz == String.class) { - return (T) value; + /** + * Parses a given String to the corresponding object defined by class. + * + * @param value Value to be parsed. + * @param clazz Class of the expected result type. + * @param Result type. + * @return Result as corresponding type. + */ + private static T parse(Object value, Class clazz) { + if (value == null) { + if (boolean.class == clazz) return (T) Boolean.FALSE; + if (byte.class == clazz) return (T) Byte.valueOf((byte) 0); + if (short.class == clazz) return (T) Short.valueOf((short) 0); + if (int.class == clazz) return (T) Integer.valueOf(0); + if (long.class == clazz) return (T) Long.valueOf(0L); + if (float.class == clazz) return (T) Float.valueOf(0); + if (double.class == clazz) return (T) Double.valueOf(0); + + return null; + } + + if (!(value instanceof String)) { + if (isBooleanOrPrimitive(clazz) && isBooleanOrPrimitive(value.getClass())) return (T) value; + if (isByteOrPrimitive(clazz) && isByteOrPrimitive(value.getClass())) return (T) value; + if (isShortOrPrimitive(clazz) && isShortOrPrimitive(value.getClass())) return (T) value; + if (isIntegerOrPrimitive(clazz) && isIntegerOrPrimitive(value.getClass())) return (T) value; + if (isLongOrPrimitive(clazz) && isLongOrPrimitive(value.getClass())) return (T) value; + if (isFloatOrPrimitive(clazz) && isFloatOrPrimitive(value.getClass())) return (T) value; + if (isDoubleOrPrimitive(clazz) && isDoubleOrPrimitive(value.getClass())) return (T) value; + } + + if (isBooleanOrPrimitive(clazz)) return (T) Boolean.valueOf(value.toString()); + if (isByteOrPrimitive(clazz)) return (T) Byte.valueOf(value.toString()); + if (isShortOrPrimitive(clazz)) return (T) Short.valueOf(value.toString()); + if (isIntegerOrPrimitive(clazz)) return (T) Integer.valueOf(value.toString()); + if (isLongOrPrimitive(clazz)) return (T) Long.valueOf(value.toString()); + if (isFloatOrPrimitive(clazz)) return (T) Float.valueOf(value.toString()); + if (isDoubleOrPrimitive(clazz)) return (T) Double.valueOf(value.toString()); + + return null; } - if (isPrimitiveOrEquivalent(clazz)) { - return parse(value, clazz); + private static boolean isBooleanOrPrimitive(Class clazz) { + return (Boolean.class == clazz) || (boolean.class == clazz); } - // Not string, not primitive, so it is a complex type: we use JSON for that. - return OBJECT_MAPPER.readValue(value, clazz); - } - - /** - * Checks if the class is a primitive or equivalent. - * @param clazz Class to be checked. - * @return True if primitive or equivalent. - */ - protected static boolean isPrimitiveOrEquivalent(Class clazz) { - if (clazz == null) { - return false; + private static boolean isByteOrPrimitive(Class clazz) { + return (Byte.class == clazz) || (byte.class == clazz); } - return (clazz.isPrimitive() || - (clazz == Boolean.class) || - (clazz == Character.class) || - (clazz == Byte.class) || - (clazz == Short.class) || - (clazz == Integer.class) || - (clazz == Long.class) || - (clazz == Float.class) || - (clazz == Double.class) || - (clazz == Void.class)); - } - - /** - * Parses a given String to the corresponding object defined by class. - * @param value String to be parsed. - * @param clazz Class of the expected result type. - * @param Result type. - * @return Result as corresponding type. - */ - protected static T parse(String value, Class clazz) { - if (value == null) { - if (boolean.class == clazz) return (T) Boolean.FALSE; - if (byte.class == clazz) return (T) Byte.valueOf((byte) 0); - if (short.class == clazz) return (T) Short.valueOf((short) 0); - if (int.class == clazz) return (T) Integer.valueOf(0); - if (long.class == clazz) return (T) Long.valueOf(0L); - if (float.class == clazz) return (T) Float.valueOf(0); - if (double.class == clazz) return (T) Double.valueOf(0); - - return null; + private static boolean isShortOrPrimitive(Class clazz) { + return (Short.class == clazz) || (short.class == clazz); } - if ((Boolean.class == clazz) || (boolean.class == clazz)) return (T) Boolean.valueOf(value); - if ((Byte.class == clazz) || (byte.class == clazz)) return (T) Byte.valueOf(value); - if ((Short.class == clazz) || (short.class == clazz)) return (T) Short.valueOf(value); - if ((Integer.class == clazz) || (int.class == clazz)) return (T) Integer.valueOf(value); - if ((Long.class == clazz) || (long.class == clazz)) return (T) Long.valueOf(value); - if ((Float.class == clazz) || (float.class == clazz)) return (T) Float.valueOf(value); - if ((Double.class == clazz) || (double.class == clazz)) return (T) Double.valueOf(value); + private static boolean isIntegerOrPrimitive(Class clazz) { + return (Integer.class == clazz) || (int.class == clazz); + } + + private static boolean isLongOrPrimitive(Class clazz) { + return (Long.class == clazz) || (long.class == clazz); + } - return null; - } + private static boolean isFloatOrPrimitive(Class clazz) { + return (Float.class == clazz) || (float.class == clazz); + } + + private static boolean isDoubleOrPrimitive(Class clazz) { + return (Double.class == clazz) || (double.class == clazz); + } } diff --git a/sdk/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java b/sdk/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java index e2e82e7183..8c49d664b2 100644 --- a/sdk/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java +++ b/sdk/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java @@ -1,7 +1,7 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.utils.ObjectSerializer; +import io.dapr.actors.runtime.ActorStateSerializer; import org.junit.Assert; import org.junit.Test; @@ -13,7 +13,7 @@ public void constructorActorProxyTest() { final ActorProxyImpl actorProxy= new ActorProxyImpl( "myActorType", new ActorId("100"), - new ObjectSerializer(), + new ActorStateSerializer(), actorProxyAsyncClient); Assert.assertEquals(actorProxy.getActorId().toString(),"100"); Assert.assertEquals(actorProxy.getActorType(),"myActorType"); diff --git a/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java b/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java index cccccd717f..a5eb85a914 100644 --- a/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java +++ b/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java @@ -4,7 +4,7 @@ */ package io.dapr.actors.client; -import io.dapr.actors.*; +import io.dapr.exceptions.DaprException; import org.junit.Assert; import org.junit.Test; diff --git a/sdk/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java b/sdk/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java new file mode 100644 index 0000000000..194d802e65 --- /dev/null +++ b/sdk/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import io.dapr.actors.ActorId; +import org.junit.Assert; +import org.junit.Test; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.mockito.Mockito.mock; + +/** + * Unit tests for Actor Manager + */ +public class ActorManagerTest { + + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); + + interface MyActor { + String say(String something); + + int getCount(); + + void incrementCount(int delta); + } + + public static class NotRemindableActor extends AbstractActor implements Actor { + public NotRemindableActor(ActorRuntimeContext runtimeContext, ActorId id) { + super(runtimeContext, id); + } + } + + @ActorType(Name = "MyActor") + public static class MyActorImpl extends AbstractActor implements Actor, MyActor, Remindable { + + private int timeCount = 0; + + @Override + public String say(String something) { + return executeSayMethod(something); + } + + @Override + public int getCount() { + return this.timeCount; + } + + @Override + public void incrementCount(int delta) { + this.timeCount = timeCount + delta; + } + + public MyActorImpl(ActorRuntimeContext runtimeContext, ActorId id) { + super(runtimeContext, id); + super.registerActorTimer( + "count", + "incrementCount", + 2, + Duration.ofSeconds(1), + Duration.ofSeconds(1) + ); + } + + @Override + public Class getStateType() { + return String.class; + } + + @Override + public Mono receiveReminder(String reminderName, String state, Duration dueTime, Duration period) { + return Mono.empty(); + } + } + + private ActorRuntimeContext context = createContext(MyActorImpl.class); + + private ActorManager manager = new ActorManager<>(context); + + @Test(expected = IllegalArgumentException.class) + public void invokeBeforeActivate() throws Exception { + ActorId actorId = newActorId(); + String message = "something"; + this.manager.invokeMethod(actorId, "say", message).block(); + } + + @Test + public void activateThenInvoke() { + ActorId actorId = newActorId(); + String message = "something"; + this.manager.activateActor(actorId).block(); + String response = this.manager.invokeMethod(actorId, "say", message).block(); + Assert.assertEquals(executeSayMethod(message), response); + } + + @Test(expected = IllegalArgumentException.class) + public void activateInvokeDeactivateThenInvoke() { + ActorId actorId = newActorId(); + String message = "something"; + this.manager.activateActor(actorId).block(); + String response = this.manager.invokeMethod(actorId, "say", message).block(); + Assert.assertEquals(executeSayMethod(message), response); + + this.manager.deactivateActor(actorId).block(); + this.manager.invokeMethod(actorId, "say", message).block(); + } + + @Test + public void invokeReminderNotRemindable() throws Exception { + ActorId actorId = newActorId(); + ActorRuntimeContext context = createContext(NotRemindableActor.class); + ActorManager manager = new ActorManager<>(context); + manager.invokeReminder(actorId, "myremind", createReminderParams("hello")).block(); + } + + @Test(expected = IllegalArgumentException.class) + public void invokeReminderBeforeActivate() throws Exception { + ActorId actorId = newActorId(); + this.manager.invokeReminder(actorId, "myremind", createReminderParams("hello")).block(); + } + + @Test + public void activateThenInvokeReminder() throws Exception { + ActorId actorId = newActorId(); + this.manager.activateActor(actorId); + this.manager.invokeReminder(actorId, "myremind", createReminderParams("hello")).block(); + } + + @Test(expected = IllegalArgumentException.class) + public void activateDeactivateThenInvokeReminder() throws Exception { + ActorId actorId = newActorId(); + this.manager.activateActor(actorId); + this.manager.deactivateActor(actorId); + this.manager.invokeReminder(actorId, "myremind", createReminderParams("hello")).block(); + } + + @Test(expected = IllegalArgumentException.class) + public void invokeTimerBeforeActivate() { + ActorId actorId = newActorId(); + this.manager.invokeTimer(actorId, "count").block(); + } + + @Test(expected = IllegalStateException.class) + public void activateThenInvokeTimerBeforeRegister() { + ActorId actorId = newActorId(); + this.manager.activateActor(actorId).block(); + this.manager.invokeTimer(actorId, "unknown").block(); + } + + @Test + public void activateThenInvokeTimer() { + ActorId actorId = newActorId(); + this.manager.activateActor(actorId).block(); + this.manager.invokeTimer(actorId, "count").block(); + String response = this.manager.invokeMethod(actorId, "getCount", null).block(); + Assert.assertEquals("2", response); + } + + @Test(expected = IllegalArgumentException.class) + public void activateInvokeTimerDeactivateThenInvokeTimer() { + ActorId actorId = newActorId(); + this.manager.activateActor(actorId).block(); + this.manager.invokeTimer(actorId, "count").block(); + String response = this.manager.invokeMethod(actorId, "getCount", null).block(); + Assert.assertEquals("2", response); + + this.manager.deactivateActor(actorId).block(); + this.manager.invokeTimer(actorId, "count").block(); + } + + private String createReminderParams(String data) throws IOException { + ActorReminderParams params = new ActorReminderParams(data, Duration.ofSeconds(1), Duration.ofSeconds(1)); + return this.context.getActorSerializer().serialize(params); + } + + private static ActorId newActorId() { + return new ActorId(Integer.toString(ACTOR_ID_COUNT.incrementAndGet())); + } + + private static String executeSayMethod(String something) { + return "Said: " + (something == null ? "" : something); + } + + private static ActorRuntimeContext createContext(Class clazz) { + return new ActorRuntimeContext( + mock(ActorRuntime.class), + new ActorStateSerializer(), + new DefaultActorFactory(), + ActorTypeInformation.create(clazz), + mock(AppToDaprAsyncClient.class), + mock(DaprStateAsyncProvider.class) + ); + } +} diff --git a/sdk/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java b/sdk/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java index f804e4e2bd..e27baf27f9 100644 --- a/sdk/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java +++ b/sdk/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java @@ -24,13 +24,13 @@ public void serialize() throws IOException { ActorTimer timer = new ActorTimer( null, "testTimer", - null, + "myfunction", null, dueTime, period); String s = new ActorStateSerializer().serialize(timer); - String expected = "{\"period\":\"1h0m3s0ms\",\"dueTime\":\"0h7m17s0ms\"}"; + String expected = "{\"period\":\"1h0m3s0ms\",\"dueTime\":\"0h7m17s0ms\", \"callback\": \"myfunction\"}"; // Deep comparison via JsonNode.equals method. Assert.assertEquals(OBJECT_MAPPER.readTree(expected), OBJECT_MAPPER.readTree(s)); } @@ -49,14 +49,14 @@ public void serializeWithOneTimePeriod() throws IOException { ActorTimer timer = new ActorTimer( null, "testTimer", - null, + "myfunction", null, dueTime, period); String s = new ActorStateSerializer().serialize(timer); // A negative period will be serialized to an empty string which is interpreted by Dapr to mean fire once only. - String expected = "{\"period\":\"\",\"dueTime\":\"0h7m17s0ms\"}"; + String expected = "{\"period\":\"\",\"dueTime\":\"0h7m17s0ms\", \"callback\": \"myfunction\"}"; // Deep comparison via JsonNode.equals method. Assert.assertEquals(OBJECT_MAPPER.readTree(expected), OBJECT_MAPPER.readTree(s)); } diff --git a/sdk/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java b/sdk/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java index 8307067661..fe5f7780d2 100644 --- a/sdk/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java +++ b/sdk/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java @@ -56,7 +56,7 @@ class A extends AbstractActor implements MyActor, Remindable { } @Override - public Class getReminderStateType() { + public Class getStateType() { return null; }