diff --git a/sdk/src/main/java/io/dapr/actors/DaprClientBuilder.java b/sdk/src/main/java/io/dapr/actors/AbstractClientBuilder.java similarity index 57% rename from sdk/src/main/java/io/dapr/actors/DaprClientBuilder.java rename to sdk/src/main/java/io/dapr/actors/AbstractClientBuilder.java index 4ac91f30a1..5af3cc78ac 100644 --- a/sdk/src/main/java/io/dapr/actors/DaprClientBuilder.java +++ b/sdk/src/main/java/io/dapr/actors/AbstractClientBuilder.java @@ -1,58 +1,49 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.actors; - -import okhttp3.OkHttpClient; - -/** - * Builds an instance of DaprAsyncClient or DaprClient. - */ -class DaprClientBuilder { - - /** - * Default port for Dapr after checking environment variable. - */ - private int port = DaprClientBuilder.GetEnvPortOrDefault(); - - /** - * Builds an async client. - * @return Builds an async client. - */ - public DaprAsyncClient buildAsyncClient() { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - // TODO: Expose configurations for OkHttpClient or com.microsoft.rest.RestClient. - return new DaprHttpAsyncClient(this.port, builder.build()); - } - - /** - * Overrides the port. - * @param port New port. - * @return This instance. - */ - public DaprClientBuilder withPort(int port) { - this.port = port; - return this; - } - - /** - * 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; - } -} +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors; + +import okhttp3.OkHttpClient; +import io.dapr.actors.*; + +/** + * 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; + } + + /** + * Tries to get a valid port from environment variable or returns default. + * @return Port defined in env variable or default. + */ + protected 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/actors/DaprHttpAsyncClient.java b/sdk/src/main/java/io/dapr/actors/AbstractDaprClient.java similarity index 51% rename from sdk/src/main/java/io/dapr/actors/DaprHttpAsyncClient.java rename to sdk/src/main/java/io/dapr/actors/AbstractDaprClient.java index b4e153e5d8..2be14579b2 100644 --- a/sdk/src/main/java/io/dapr/actors/DaprHttpAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/AbstractDaprClient.java @@ -1,206 +1,131 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.actors; - -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.*; -import reactor.core.publisher.Mono; - -import java.io.IOException; -import java.net.URL; -import java.util.UUID; - -/** - * Http client to call Dapr's API for actors. - */ -class DaprHttpAsyncClient implements DaprAsyncClient { - - /** - * 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(); - /** - * Base Url for calling Dapr. (e.g. http://localhost:3500/) - */ - private final String baseUrl; - - /** - * Http client used for all API calls. - */ - private final OkHttpClient httpClient; - - /** - * Creates a new instance of {@link DaprHttpAsyncClient}. - * @param port Port for calling Dapr. (e.g. 3500) - * @param httpClient RestClient used for all API calls in this new instance. - */ - DaprHttpAsyncClient(int port, OkHttpClient httpClient) - { - this.baseUrl = String.format("http://%s:%d/", Constants.DEFAULT_HOSTNAME, port);; - this.httpClient = 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 invokeAPI("PUT", url, jsonPayload); - } - - /** - * {@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 invokeAPI("GET", url, null); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono removeState(String actorType, String actorId, String keyName) { - String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); - return invokeAPIVoid("DELETE", 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 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 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 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 invokeAPIVoid("PUT", url, data); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono unregisterTimerAsync(String actorType, String actorId, String timerName) { - String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); - return invokeAPIVoid("DELETE", url, null); - } - - /** - * 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 - */ - private 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 - */ - private final Mono invokeAPI(String method, String urlString, String json) { - return Mono.fromSupplier(() -> { - try { - return tryInvokeAPI(method, urlString, json); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - }); - } - - /** - * Invokes an API synchronously and returns a text payload. - * @param method HTTP method. - * @param urlString url as String. - * @param json JSON payload or null. - * @return text - */ - private final String tryInvokeAPI(String method, String urlString, String json) throws IOException { - 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(); - - // TODO: make this call async as well. - 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 DaprException(error); - } - - throw new DaprException("UNKNOWN", String.format("Dapr's Actor API %s failed with return code %d %s", urlString, response.code())); - } - - return response.body().string(); - } - - /** - * Tries to parse an error from Dapr response body. - * @param json Response body from Dapr. - * @return DaprError or null if could not parse. - */ - private static DaprError parseDaprError(String json) { - if (json == null) { - return null; - } - - try { - return OBJECT_MAPPER.readValue(json, DaprError.class); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } -} +package io.dapr.actors; + + + +import com.fasterxml.jackson.databind.ObjectMapper; + +import okhttp3.*; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.net.URL; +import java.util.UUID; + +// base class of hierarchy +public abstract class AbstractDaprClient { + /** + * 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 AbstractDaprClient}. + * @param port Port for calling Dapr. (e.g. 3500) + * @param httpClient RestClient used for all API calls in this new instance. + */ + public AbstractDaprClient(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 + */ + protected final Mono invokeAPI(String method, String urlString, String json) { + return Mono.fromSupplier(() -> { + try { + return tryInvokeAPI(method, urlString, json); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + }); + } + + /** + * Invokes an API synchronously and returns a text payload. + * @param method HTTP method. + * @param urlString url as String. + * @param json JSON payload or null. + * @return text + */ + protected final String tryInvokeAPI(String method, String urlString, String json) throws IOException { + 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(); + + // TODO: make this call async as well. + 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 DaprException(error); + } + + throw new DaprException("UNKNOWN", String.format("Dapr's Actor API %s failed with return code %d %s", urlString, response.code())); + } + + return response.body().string(); + } + + /** + * 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; + } + + try { + return OBJECT_MAPPER.readValue(json, DaprError.class); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/sdk/src/main/java/io/dapr/actors/ActorId.java b/sdk/src/main/java/io/dapr/actors/ActorId.java new file mode 100644 index 0000000000..412f729452 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/ActorId.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors; + +/** + * Stub + */ +public class ActorId { + public ActorId(String id) { + } +} diff --git a/sdk/src/main/java/io/dapr/actors/ActorTrace.java b/sdk/src/main/java/io/dapr/actors/ActorTrace.java new file mode 100644 index 0000000000..f3c7d5fea1 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/ActorTrace.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors; + +/** + * Stub + */ +public class ActorTrace { + public static void WriteInfo(String text) { + System.out.println(text); + } + + public static void WriteWarning(String text) { + System.out.println("Warning: " + text); + } + + public static void WriteError(String text) { + System.err.println(text); + } +} diff --git a/sdk/src/main/java/io/dapr/actors/Constants.java b/sdk/src/main/java/io/dapr/actors/Constants.java index 66f4dbfc3f..86ea80fac8 100644 --- a/sdk/src/main/java/io/dapr/actors/Constants.java +++ b/sdk/src/main/java/io/dapr/actors/Constants.java @@ -8,7 +8,7 @@ /** * Useful constants for the Dapr's Actor SDK. */ -final class Constants { +public final class Constants { /** * Dapr API used in this client. diff --git a/sdk/src/main/java/io/dapr/actors/DaprException.java b/sdk/src/main/java/io/dapr/actors/DaprException.java index 2c7b8348f7..50ad94a9b9 100644 --- a/sdk/src/main/java/io/dapr/actors/DaprException.java +++ b/sdk/src/main/java/io/dapr/actors/DaprException.java @@ -10,7 +10,7 @@ /** * A Dapr's specific exception. */ -class DaprException extends IOException { +public class DaprException extends IOException { /** * Dapr's error code for this exception. @@ -39,7 +39,7 @@ class DaprException extends IOException { * Returns the exception's error code. * @return Error code. */ - String getErrorCode() { + public String getErrorCode() { return this.errorCode; } } diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java new file mode 100644 index 0000000000..b4f6b0bfda --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyAsyncClient.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.client; + +import reactor.core.publisher.Mono; + +/** + * Interface to invoke actor methods. + */ +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); +} diff --git a/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java b/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java new file mode 100644 index 0000000000..4f7d88fb9f --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyClientBuilder.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.client; + +import okhttp3.OkHttpClient; +import io.dapr.actors.*; + +/** + * Builds an instance of ActorProxyAsyncClient. + */ +class ActorProxyClientBuilder extends AbstractClientBuilder { + + /** + * Default port for Dapr after checking environment variable. + */ + private int port = ActorProxyClientBuilder.GetEnvPortOrDefault(); + + /** + * 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(this.port, 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 new file mode 100644 index 0000000000..2b44b35eab --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/client/ActorProxyHttpAsyncClient.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.client; + +import io.dapr.actors.*; +import io.dapr.actors.AbstractDaprClient; +import okhttp3.*; +import reactor.core.publisher.Mono; + +/** + * Http client to call actors methods. + */ +class ActorProxyHttpAsyncClient extends AbstractDaprClient 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. + */ + public 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); + } +} 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 81eeaac341..1162c254f2 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AbstractActor.java @@ -6,7 +6,7 @@ package io.dapr.actors.runtime; /** - * TODO + * TODO - this is the base class Actor implementations (user code) will extend. */ public abstract class AbstractActor { } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/Actor.java b/sdk/src/main/java/io/dapr/actors/runtime/Actor.java index 25c8193252..abc4af567e 100644 --- a/sdk/src/main/java/io/dapr/actors/runtime/Actor.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/Actor.java @@ -6,7 +6,7 @@ package io.dapr.actors.runtime; /** - * TODO + * TODO - this is the interface user Actor methods should implement to receive calls. */ public interface Actor { } diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java new file mode 100644 index 0000000000..3974dd43a6 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorManager.java @@ -0,0 +1,11 @@ +package io.dapr.actors.runtime; + +// stub +public class ActorManager { + + public ActorManager(ActorService actorService) { + + } +} + + diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java new file mode 100644 index 0000000000..6574cb8658 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorRuntime.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; +import java.util.function.Function; + +import io.dapr.actors.*; + +/** + * Contains methods to register actor types. Registering the types allows the runtime to create instances of the actor. + */ +public class ActorRuntime { + /** + * 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 static AppToDaprAsyncClient appToDaprAsyncClient; + + /** + * A trace type used when logging. + */ + private static final String TraceType = "ActorRuntime"; + + /** + * Map of ActorType --> ActorManager. + */ + private final HashMap 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 = new HashMap(); + appToDaprAsyncClient = new AppToDaprClientBuilder().buildAsyncClient(); + } + + /** + * Returns an ActorRuntime object. + * @return An ActorRuntime object. + */ + public static ActorRuntime getInstance() { + if (instance == null) { + synchronized (ActorRuntime.class) { + if (instance == null) { + instance = new ActorRuntime(); + } + } + } + + return instance; + } + + /** + * + * @return Actor type names registered with the runtime. + */ + public Collection getRegisteredActorTypes() { + return Collections.unmodifiableCollection(this.actorManagers.keySet()); + } + + /** + * Registers an actor with the runtime. + * @param clazz The type of actor. + */ + public void RegisterActor(Class clazz) { + RegisterActor(clazz, null); + } + + /** + * Registers an actor with the runtime. + * @param clazz The type of actor. + * @param actorServiceFactory An optional delegate to create actor service. This can be used for dependency injection into actors. + */ + public void RegisterActor(Class clazz, Function actorServiceFactory) + { + ActorTypeInformation actorTypeInfo = ActorTypeInformation.create(clazz); + + ActorService actorService; + if (actorServiceFactory != null) + { + actorService = actorServiceFactory.apply(actorTypeInfo); + } + else + { + actorService = new ActorService(actorTypeInfo); + } + + // Create ActorManagers, override existing entry if registered again. + synchronized (this.actorManagers) { + this.actorManagers.put(actorTypeInfo.getName(), new ActorManager(actorService)); + } + } + + /** + * 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. + */ + static void Activate(String actorTypeName, String actorId) + { + // uncomment when ActorManager implemented + // return instance.GetActorManager(actorTypeName).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. + */ + static void Deactivate(String actorTypeName, String actorId) + { + // uncomment when ActorManager implemented + // return instance.GetActorManager(actorTypeName).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 requestBodyStream Payload for the actor method. + * @param responseBodyStream Response for the actor method. + * @return + */ + static void Dispatch(String actorTypeName, String actorId, String actorMethodName, byte[] requestBodyStream, byte[] responseBodyStream) + { + // uncomment when ActorManager implemented + // return instance.GetActorManager(actorTypeName).Dispatch(new ActorId(actorId), actorMethodName, requestBodyStream, responseBodyStream); + } + + /** + * 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 requestBodyStream Payload for the actor method + */ + static void FireReminder(String actorTypeName, String actorId, String reminderName, byte[] requestBodyStream) + { + // uncomment when ActorManager implemented + // return instance.GetActorManager(actorTypeName).FireReminder(new ActorId(actorId), reminderName, requestBodyStream); + } + + /** + * 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. + */ + static void FireTimer(String actorTypeName, String actorId, String timerName) + { + // uncomment when ActorManager implemented + // return instance.GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), timerName); + } + + private ActorManager GetActorManager(String actorTypeName) throws IllegalStateException + { + ActorManager actorManager = this.actorManagers.get(actorTypeName); + + if (actorManager == null) + { + String errorMsg = String.format("Actor type %s is not registered with Actor runtime.", actorTypeName); + + ActorTrace.WriteError(errorMsg); + throw new IllegalStateException(errorMsg); + } + + return actorManager; + } +} \ No newline at end of file diff --git a/sdk/src/main/java/io/dapr/actors/runtime/ActorService.java b/sdk/src/main/java/io/dapr/actors/runtime/ActorService.java new file mode 100644 index 0000000000..110f6fc248 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/ActorService.java @@ -0,0 +1,8 @@ +package io.dapr.actors.runtime; + +// stub +public class ActorService { + public ActorService(ActorTypeInformation actorTypeInformation) { + + } +} diff --git a/sdk/src/main/java/io/dapr/actors/DaprAsyncClient.java b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java similarity index 69% rename from sdk/src/main/java/io/dapr/actors/DaprAsyncClient.java rename to sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java index 9b0345f429..66fe3d1757 100644 --- a/sdk/src/main/java/io/dapr/actors/DaprAsyncClient.java +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprAsyncClient.java @@ -1,89 +1,71 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.actors; - -import reactor.core.publisher.Mono; - -/** - * Interface for interacting with Dapr runtime. - */ -interface DaprAsyncClient { - - /** - * 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); - - /** - * 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); - - /** - * Removes Actor state in Dapr. This is temporary until the Dapr runtime implements the Batch state update. - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param keyName State name. - * @return Asynchronous void result. - */ - Mono removeState(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); - - /** - * 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); - - /** - * 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 unregisterTimerAsync(String actorType, String actorId, String timerName); -} +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import reactor.core.publisher.Mono; + + +/** + * Interface for interacting from the actor app to the Dapr runtime. + */ +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); + + /** + * 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); + + /** + * 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); + + /** + * 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 unregisterTimerAsync(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 new file mode 100644 index 0000000000..1476057ca3 --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprClientBuilder.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import okhttp3.OkHttpClient; +import io.dapr.actors.*; + +/** + * Builds an instance of AppToDaprAsyncClient. + */ +class AppToDaprClientBuilder extends AbstractClientBuilder { + + /** + * Default port for Dapr after checking environment variable. + */ + private int port = AppToDaprClientBuilder.GetEnvPortOrDefault(); + + /** + * 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(this.port, 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 new file mode 100644 index 0000000000..6c4041042b --- /dev/null +++ b/sdk/src/main/java/io/dapr/actors/runtime/AppToDaprHttpAsyncClient.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.runtime; + +import io.dapr.actors.Constants; +import io.dapr.actors.AbstractDaprClient; +import okhttp3.*; +import reactor.core.publisher.Mono; + + +/** + * Http client to call Dapr's API for actors. + */ +//public class DaprHttpAsyncClient implements DaprAsyncClient { +class AppToDaprHttpAsyncClient extends AbstractDaprClient implements AppToDaprAsyncClient { + + /** + * 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 unregisterTimerAsync(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); + } +} diff --git a/sdk/src/test/java/io/dapr/actors/DaprHttpAsyncClientIT.java b/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java similarity index 89% rename from sdk/src/test/java/io/dapr/actors/DaprHttpAsyncClientIT.java rename to sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java index 763f8e38b9..3096f13a89 100644 --- a/sdk/src/test/java/io/dapr/actors/DaprHttpAsyncClientIT.java +++ b/sdk/src/test/java/io/dapr/actors/client/DaprHttpAsyncClientIT.java @@ -1,42 +1,43 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.actors; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Integration test for the HTTP Async Client. - * - * Requires Dapr running. - */ -public class DaprHttpAsyncClientIT { - - /** - * Checks if the error is correctly parsed when trying to invoke a function on an unknown actor type. - */ - @Test(expected = RuntimeException.class) - public void invokeUnknownActor() { - DaprAsyncClient daprAsyncClient = new DaprClientBuilder().buildAsyncClient(); - daprAsyncClient - .invokeActorMethod("ActorThatDoesNotExist", "100", "GetData", null) - .doOnError(x -> { - Assert.assertTrue(x instanceof RuntimeException); - RuntimeException runtimeException = (RuntimeException)x; - - Throwable cause = runtimeException.getCause(); - Assert.assertTrue(cause instanceof DaprException); - DaprException daprException = (DaprException)cause; - - Assert.assertNotNull(daprException); - Assert.assertEquals("ERR_INVOKE_ACTOR", daprException.getErrorCode()); - Assert.assertNotNull(daprException.getMessage()); - Assert.assertFalse(daprException.getMessage().isEmpty()); - }) - .doOnSuccess(x -> Assert.fail("This call should fail.")) - .block(); - } -} +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.client; + +import io.dapr.actors.*; +import org.junit.Assert; +import org.junit.Test; + +/** + * Integration test for the HTTP Async Client. + * + * Requires Dapr running. + */ +public class DaprHttpAsyncClientIT { + + /** + * Checks if the error is correctly parsed when trying to invoke a function on an unknown actor type. + */ + @Test(expected = RuntimeException.class) + public void invokeUnknownActor() { + ActorProxyAsyncClient daprAsyncClient = new ActorProxyClientBuilder().buildAsyncClient(); + daprAsyncClient + .invokeActorMethod("ActorThatDoesNotExist", "100", "GetData", null) + .doOnError(x -> { + Assert.assertTrue(x instanceof RuntimeException); + RuntimeException runtimeException = (RuntimeException)x; + + Throwable cause = runtimeException.getCause(); + Assert.assertTrue(cause instanceof DaprException); + DaprException daprException = (DaprException)cause; + + Assert.assertNotNull(daprException); + Assert.assertEquals("ERR_INVOKE_ACTOR", daprException.getErrorCode()); + Assert.assertNotNull(daprException.getMessage()); + Assert.assertFalse(daprException.getMessage().isEmpty()); + }) + .doOnSuccess(x -> Assert.fail("This call should fail.")) + .block(); + } +}