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 30546ea1c3..6cf19f784f 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 @@ -5,6 +5,8 @@ package io.dapr.examples.actors.http; +import reactor.core.publisher.Mono; + /** * Example of implementation of an Actor. */ @@ -15,4 +17,6 @@ public interface DemoActor { String say(String something); void clock(String message); + + Mono incrementAndGet(int delta); } 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 5faebb051f..9387b0be28 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,6 +8,7 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyBuilder; +import io.dapr.client.DefaultObjectSerializer; import java.util.ArrayList; import java.util.List; @@ -34,12 +35,12 @@ public class DemoActorClient { private static final ExecutorService POOL = Executors.newFixedThreadPool(NUM_ACTORS); public static void main(String[] args) throws Exception { - ActorProxyBuilder builder = new ActorProxyBuilder(); + ActorProxyBuilder builder = new ActorProxyBuilder("DemoActor", new DefaultObjectSerializer()); List> futures = new ArrayList<>(NUM_ACTORS); for (int i = 0; i < NUM_ACTORS; i++) { - ActorProxy actor = builder.withActorType("DemoActor").withActorId(ActorId.createRandom()).build(); + ActorProxy actor = builder.build(ActorId.createRandom()); futures.add(callActorNTimes(actor)); } @@ -54,6 +55,7 @@ private static final CompletableFuture callActorNTimes(ActorProxy actor) { return CompletableFuture.runAsync(() -> { actor.invokeActorMethod("registerReminder").block(); for (int i = 0; i < NUM_MESSAGES_PER_ACTOR; i++) { + actor.invokeActorMethod("incrementAndGet", 1).block(); String result = actor.invokeActorMethod(METHOD_NAME, 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)); @@ -65,6 +67,9 @@ private static final CompletableFuture callActorNTimes(ActorProxy actor) { return; } } + + System.out.println( + "Messages sent: " + actor.invokeActorMethod("incrementAndGet", 0, int.class).block()); }, POOL); } } 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 f36246b46c..3b91ae29de 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,7 +6,10 @@ package io.dapr.examples.actors.http; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.*; +import io.dapr.actors.runtime.AbstractActor; +import io.dapr.actors.runtime.ActorRuntimeContext; +import io.dapr.actors.runtime.ActorType; +import io.dapr.actors.runtime.Remindable; import reactor.core.publisher.Mono; import java.text.DateFormat; @@ -18,8 +21,8 @@ /** * Implementation of the DemoActor for the server side. */ -@ActorType(Name = "DemoActor") -public class DemoActorImpl extends AbstractActor implements DemoActor, Actor, Remindable { +@ActorType(name = "DemoActor") +public class DemoActorImpl extends AbstractActor implements DemoActor, Remindable { /** * Format to output date and time. @@ -56,10 +59,20 @@ public String say(String something) { super.getId() + ": " + (something == null ? "" : something + " @ " + utcNowAsString)); + super.getActorStateManager().set("lastmessage", something).block(); + // Now respond with current timestamp. return utcNowAsString; } + @Override + public Mono incrementAndGet(int delta) { + return super.getActorStateManager().contains("counter") + .flatMap(exists -> exists ? super.getActorStateManager().get("counter", int.class) : Mono.just(0)) + .map(c -> c + delta) + .flatMap(c -> super.getActorStateManager().set("counter", c).thenReturn(c)); + } + @Override public void clock(String message) { Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 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 83299d40ff..be8539e20b 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 @@ -6,6 +6,7 @@ package io.dapr.examples.actors.http; import io.dapr.actors.runtime.ActorRuntime; +import io.dapr.client.DefaultObjectSerializer; import io.dapr.springboot.DaprApplication; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -35,7 +36,7 @@ public static void main(String[] args) throws Exception { int port = Integer.parseInt(cmd.getOptionValue("port")); // Register the Actor class. - ActorRuntime.getInstance().registerActor(DemoActorImpl.class); + ActorRuntime.getInstance().registerActor(DemoActorImpl.class, new DefaultObjectSerializer()); // Start Dapr's callback endpoint. DaprApplication.start(port); diff --git a/examples/src/main/java/io/dapr/examples/bindings/http/OutputBindingExample.java b/examples/src/main/java/io/dapr/examples/bindings/http/OutputBindingExample.java index 2521f9ee32..d91dcac117 100644 --- a/examples/src/main/java/io/dapr/examples/bindings/http/OutputBindingExample.java +++ b/examples/src/main/java/io/dapr/examples/bindings/http/OutputBindingExample.java @@ -7,7 +7,7 @@ import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; -import io.dapr.utils.ObjectSerializer; +import io.dapr.client.DefaultObjectSerializer; /** * Service for output binding example. @@ -25,7 +25,7 @@ public MyClass(){} } public static void main(String[] args) throws Exception { - DaprClient client = new DaprClientBuilder().build(); + DaprClient client = new DaprClientBuilder(new DefaultObjectSerializer()).build(); final String BINDING_NAME = "sample123"; diff --git a/examples/src/main/java/io/dapr/examples/invoke/http/InvokeClient.java b/examples/src/main/java/io/dapr/examples/invoke/http/InvokeClient.java index f9123594fd..7037439fef 100644 --- a/examples/src/main/java/io/dapr/examples/invoke/http/InvokeClient.java +++ b/examples/src/main/java/io/dapr/examples/invoke/http/InvokeClient.java @@ -7,6 +7,7 @@ import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DefaultObjectSerializer; import io.dapr.client.domain.Verb; /** @@ -27,7 +28,7 @@ public class InvokeClient { * @param args Messages to be sent as request for the invoke API. */ public static void main(String[] args) { - DaprClient client = (new DaprClientBuilder()).build(); + DaprClient client = (new DaprClientBuilder(new DefaultObjectSerializer())).build(); for (String message : args) { client.invokeService(Verb.POST, SERVICE_APP_ID, "say", message, null, String.class).block(); } diff --git a/examples/src/main/java/io/dapr/examples/pubsub/http/Publisher.java b/examples/src/main/java/io/dapr/examples/pubsub/http/Publisher.java index 56b2d4249e..0da5802a8c 100644 --- a/examples/src/main/java/io/dapr/examples/pubsub/http/Publisher.java +++ b/examples/src/main/java/io/dapr/examples/pubsub/http/Publisher.java @@ -7,6 +7,7 @@ import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DefaultObjectSerializer; import java.util.Collections; @@ -24,7 +25,7 @@ public class Publisher { private static final String TOPIC_NAME = "message"; public static void main(String[] args) throws Exception { - DaprClient client = new DaprClientBuilder().build(); + DaprClient client = new DaprClientBuilder(new DefaultObjectSerializer()).build(); for (int i = 0; i < NUM_MESSAGES; i++) { String message = String.format("This is message #%d", i); client.publishEvent(TOPIC_NAME, message).block(); diff --git a/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java b/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java index adc6957097..e9ca900072 100644 --- a/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java +++ b/examples/src/main/java/io/dapr/examples/pubsub/http/SubscriberController.java @@ -5,8 +5,8 @@ package io.dapr.examples.pubsub.http; -import io.dapr.client.domain.CloudEventEnvelope; -import io.dapr.utils.ObjectSerializer; +import io.dapr.client.DefaultObjectSerializer; +import io.dapr.client.domain.CloudEvent; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; @@ -21,7 +21,7 @@ public class SubscriberController { /** * Dapr's default serializer/deserializer. */ - private static final ObjectSerializer SERIALIZER = new ObjectSerializer(); + private static final DefaultObjectSerializer SERIALIZER = new DefaultObjectSerializer(); @GetMapping("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/dapr/subscribe") public byte[] daprConfig() throws Exception { @@ -34,7 +34,7 @@ public Mono handleMessage(@RequestBody(required = false) byte[] body, return Mono.fromRunnable(() -> { try { // Dapr's event is compliant to CloudEvent. - CloudEventEnvelope envelope = SERIALIZER.deserialize(body, CloudEventEnvelope.class); + CloudEvent envelope = CloudEvent.deserialize(body); String message = envelope.getData() == null ? "" : new String(envelope.getData()); System.out.println("Subscriber got message: " + message); diff --git a/examples/src/main/java/io/dapr/springboot/DaprController.java b/examples/src/main/java/io/dapr/springboot/DaprController.java index 79a32c7fd0..1f1ffa018b 100644 --- a/examples/src/main/java/io/dapr/springboot/DaprController.java +++ b/examples/src/main/java/io/dapr/springboot/DaprController.java @@ -5,9 +5,7 @@ package io.dapr.springboot; -import com.fasterxml.jackson.databind.ObjectMapper; import io.dapr.actors.runtime.ActorRuntime; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; @@ -17,18 +15,13 @@ @RestController public class DaprController { - @Autowired - private ObjectMapper objectMapper; - - private String topics; - @GetMapping("/") public String index() { return "Greetings from Dapr!"; } @GetMapping("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/dapr/config") - public String daprConfig() throws Exception { + public byte[] daprConfig() throws Exception { return ActorRuntime.getInstance().serializeConfig(); } @@ -45,10 +38,10 @@ public Mono deactivateActor(@PathVariable("type") String type, } @PutMapping(path = "/actors/{type}/{id}/method/{method}") - public Mono invokeActorMethod(@PathVariable("type") String type, + public Mono invokeActorMethod(@PathVariable("type") String type, @PathVariable("id") String id, @PathVariable("method") String method, - @RequestBody(required = false) String body) { + @RequestBody(required = false) byte[] body) { return ActorRuntime.getInstance().invoke(type, id, method, body); } @@ -63,7 +56,7 @@ public Mono invokeActorTimer(@PathVariable("type") String type, public Mono invokeActorReminder(@PathVariable("type") String type, @PathVariable("id") String id, @PathVariable("reminder") String reminder, - @RequestBody(required = false) String body) { + @RequestBody(required = false) byte[] body) { return ActorRuntime.getInstance().invokeReminder(type, id, reminder, body); } diff --git a/sdk-actors/pom.xml b/sdk-actors/pom.xml index af8cf95245..476e61ef2f 100644 --- a/sdk-actors/pom.xml +++ b/sdk-actors/pom.xml @@ -48,6 +48,18 @@ mockito-core test + + com.github.gmazzo + okhttp-mock + 1.3.2 + test + + + org.junit.jupiter + junit-jupiter-api + 5.5.2 + test + diff --git a/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java b/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java index 03c30e89d7..0623403cf8 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java +++ b/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyBuilder.java @@ -1,77 +1,63 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.ActorStateSerializer; -import io.dapr.client.DaprClientBuilder; -import okhttp3.OkHttpClient; +import io.dapr.client.DaprHttpBuilder; +import io.dapr.client.DaprObjectSerializer; /** - * Builder to generate an ActorProxy instance. + * Builder to generate an ActorProxy instance. Builder can be reused for multiple instances. */ public class ActorProxyBuilder { /** - * Serializer for content to be sent back and forth between actors. + * Builder for Dapr's raw http client. */ - private static final ActorStateSerializer SERIALIZER = new ActorStateSerializer(); - - /** - * Builder for the Dapr client. - */ - private final DaprClientBuilder clientBuilder = new DaprClientBuilder(); + private final DaprHttpBuilder daprHttpBuilder = new DaprHttpBuilder(); /** * Actor's type. */ - private String actorType; + private final String actorType; /** - * Actor's identifier. + * Dapr's object serializer. */ - private ActorId actorId; + private final DaprObjectSerializer serializer; /** - * Changes build config to use given Actor's type. + * Instantiates a new builder for a given Actor type. * * @param actorType Actor's type. - * @return Same builder object. + * @param serializer Serializer for objects sent/received. Use null for default (not recommended). */ - public ActorProxyBuilder withActorType(String actorType) { - this.actorType = actorType; - return this; - } + public ActorProxyBuilder(String actorType, DaprObjectSerializer serializer) { + if ((actorType == null) || actorType.isEmpty()) { + throw new IllegalArgumentException("ActorType is required."); + } + if (serializer == null) { + throw new IllegalArgumentException("Serializer is required."); + } - /** - * 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; + this.actorType = actorType; + this.serializer = serializer; } /** * Instantiates a new ActorProxy. * + * @param actorId Actor's identifier. * @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) { + public ActorProxy build(ActorId actorId) { + if (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.build()); + actorId, + this.serializer, + new DaprHttpClient(this.daprHttpBuilder.build())); } } diff --git a/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyImpl.java b/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyImpl.java index a0d6a9ec1f..59665c12bf 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyImpl.java +++ b/sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyImpl.java @@ -1,8 +1,8 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.ActorStateSerializer; -import io.dapr.client.DaprClient; +import io.dapr.actors.runtime.ObjectSerializer; +import io.dapr.client.DaprObjectSerializer; import reactor.core.publisher.Mono; import java.io.IOException; @@ -12,6 +12,11 @@ */ class ActorProxyImpl implements ActorProxy { + /** + * Serializer used for internal objects. + */ + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + /** * Actor's identifier for this Actor instance. */ @@ -25,7 +30,7 @@ class ActorProxyImpl implements ActorProxy { /** * Serializer/deserialzier to exchange message for Actors. */ - private final ActorStateSerializer serializer; + private final DaprObjectSerializer serializer; /** * Client to talk to the Dapr's API. @@ -40,7 +45,7 @@ class ActorProxyImpl implements ActorProxy { * @param serializer Serializer and deserializer for method calls. * @param daprClient Dapr client. */ - ActorProxyImpl(String actorType, ActorId actorId, ActorStateSerializer serializer, DaprClient daprClient) { + ActorProxyImpl(String actorType, ActorId actorId, DaprObjectSerializer serializer, DaprClient daprClient) { this.actorType = actorType; this.actorId = actorId; this.daprClient = daprClient; @@ -67,7 +72,7 @@ public String getActorType() { @Override public Mono invokeActorMethod(String methodName, Object data, Class clazz) { return this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, this.wrap(data)) - .filter(s -> (s != null) && (!s.isEmpty())) + .filter(s -> s.length > 0) .map(s -> unwrap(s, clazz)); } @@ -77,7 +82,7 @@ public Mono invokeActorMethod(String methodName, Object data, Class cl @Override public Mono invokeActorMethod(String methodName, Class clazz) { return this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null) - .filter(s -> (s != null) && (!s.isEmpty())) + .filter(s -> s.length > 0) .map(s -> unwrap(s, clazz)); } @@ -100,15 +105,15 @@ public Mono invokeActorMethod(String methodName, Object data) { /** * Extracts the response object from the Actor's method result. * - * @param response String returned by API. + * @param response response returned by API. * @param clazz Expected response class. * @param Expected response type. * @return Response object or null. * @throws RuntimeException In case it cannot generate Object. */ - private T unwrap(final String response, Class clazz) { + private T unwrap(final byte[] response, Class clazz) { try { - return this.serializer.deserialize(this.serializer.unwrapData(response), clazz); + return this.serializer.deserialize(INTERNAL_SERIALIZER.unwrapData(response), clazz); } catch (IOException e) { throw new RuntimeException(e); } @@ -118,13 +123,12 @@ private T unwrap(final String response, Class 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 RuntimeException In case it cannot generate String. + * @return Payload to be sent to Dapr's API. + * @throws RuntimeException In case it cannot generate payload. */ - private String wrap(final T request) { + private byte[] wrap(final Object request) { try { - return this.serializer.wrapData(this.serializer.serialize(request)); + return INTERNAL_SERIALIZER.wrapData(this.serializer.serialize(request)); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/sdk-actors/src/main/java/io/dapr/actors/client/DaprClient.java b/sdk-actors/src/main/java/io/dapr/actors/client/DaprClient.java new file mode 100644 index 0000000000..673709701a --- /dev/null +++ b/sdk-actors/src/main/java/io/dapr/actors/client/DaprClient.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.client; + +import reactor.core.publisher.Mono; + +/** + * Generic Client Adapter to be used regardless of the GRPC or the HTTP Client implementation required. + */ +interface DaprClient { + + /** + * 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, byte[] jsonPayload); + +} diff --git a/sdk-actors/src/main/java/io/dapr/actors/client/DaprHttpClient.java b/sdk-actors/src/main/java/io/dapr/actors/client/DaprHttpClient.java new file mode 100644 index 0000000000..46f6433dd6 --- /dev/null +++ b/sdk-actors/src/main/java/io/dapr/actors/client/DaprHttpClient.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.client; + +import io.dapr.client.DaprHttp; +import io.dapr.utils.Constants; +import reactor.core.publisher.Mono; + +/** + * DaprClient over HTTP for actor client. + * + * @see DaprHttp + */ +class DaprHttpClient implements DaprClient { + + /** + * The HTTP client to be used + * + * @see DaprHttp + */ + private final DaprHttp client; + + /** + * Instantiates a new Dapr Http Client to invoke Actors + * + * @param client Dapr's http client. + */ + DaprHttpClient(DaprHttp client) { + this.client = client; + } + + /** + * {@inheritDoc} + */ + @Override + public Mono invokeActorMethod(String actorType, String actorId, String methodName, byte[] jsonPayload) { + String url = String.format(Constants.ACTOR_METHOD_RELATIVE_URL_FORMAT, actorType, actorId, methodName); + Mono responseMono = + this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, null, jsonPayload, null); + return responseMono.map(r -> r.getBody()); + } + +} diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/AbstractActor.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/AbstractActor.java index d47709a1ac..5df2ed1376 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/AbstractActor.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/AbstractActor.java @@ -17,12 +17,13 @@ /** * Represents the base class for actors. *

- * The base type for actors, that provides the common functionality - * for actors that derive from {@link Actor}. + * The base type for actors, that provides the common functionality for actors. * The state is preserved across actor garbage collections and fail-overs. */ public abstract class AbstractActor { + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + /** * Type of tracing messages. */ @@ -53,6 +54,11 @@ public abstract class AbstractActor { */ private final ActorStateManager actorStateManager; + /** + * Internal control to assert method invocation on start and finish in this SDK. + */ + private boolean started; + /** * Instantiates a new Actor. * @@ -68,6 +74,7 @@ protected AbstractActor(ActorRuntimeContext runtimeContext, ActorId id) { id); this.actorTrace = runtimeContext.getActorTrace(); this.timers = Collections.synchronizedMap(new HashMap<>()); + this.started = false; } /** @@ -104,9 +111,9 @@ protected Mono registerReminder( Duration dueTime, Duration period) { try { - String data = this.actorRuntimeContext.getActorSerializer().serializeString(state); + byte[] data = this.actorRuntimeContext.getObjectSerializer().serialize(state); ActorReminderParams params = new ActorReminderParams(data, dueTime, period); - String serialized = this.actorRuntimeContext.getActorSerializer().serializeString(params); + byte[] serialized = INTERNAL_SERIALIZER.serialize(params); return this.actorRuntimeContext.getDaprClient().registerActorReminder( this.actorRuntimeContext.getActorTypeInformation().getName(), this.id.toString(), @@ -156,7 +163,7 @@ protected Mono registerActorTimer( this.actorRuntimeContext.getActorTypeInformation().getName(), this.id.toString(), actorTimer.getName(), - this.actorRuntimeContext.getActorSerializer().serializeString(actorTimer)); + this.actorRuntimeContext.getObjectSerializer().serialize(actorTimer)); } catch (Exception e) { return Mono.error(e); } @@ -238,6 +245,18 @@ protected Mono saveState() { return this.actorStateManager.save(); } + /** + * Resets the cached state of this Actor. + */ + void rollback() { + if (!this.started) { + throw new IllegalStateException("Cannot reset state before starting call."); + } + + this.resetState(); + this.started = false; + } + /** * Resets the cached state of this Actor. */ @@ -289,7 +308,13 @@ Mono onDeactivateInternal() { * @return Asynchronous void response. */ Mono onPreActorMethodInternal(ActorMethodContext actorMethodContext) { - return this.onPreActorMethod(actorMethodContext); + return Mono.fromRunnable(() -> { + if (this.started) { + throw new IllegalStateException("Cannot invoke a method before completing previous call."); + } + + this.started = true; + }).then(this.onPreActorMethod(actorMethodContext)); } /** @@ -299,7 +324,15 @@ Mono onPreActorMethodInternal(ActorMethodContext actorMethodContext) { * @return Asynchronous void response. */ Mono onPostActorMethodInternal(ActorMethodContext actorMethodContext) { - return this.onPostActorMethod(actorMethodContext).then(this.saveState()); + return Mono.fromRunnable(() -> { + if (!this.started) { + throw new IllegalStateException("Cannot complete a method before starting a call."); + } + }).then(this.onPostActorMethod(actorMethodContext)) + .then(this.saveState()) + .then(Mono.fromRunnable(() -> { + this.started = false; + })); } /** diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/Actor.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/Actor.java deleted file mode 100644 index ba80afc6c6..0000000000 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/Actor.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ -package io.dapr.actors.runtime; - -/** - * Base interface for inheriting reliable actor interfaces. - */ -public interface Actor { -} diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorManager.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorManager.java index eda8c2473f..032d0c9f25 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorManager.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorManager.java @@ -2,10 +2,8 @@ import io.dapr.actors.ActorId; import reactor.core.publisher.Mono; -import reactor.core.publisher.SignalType; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; @@ -17,6 +15,11 @@ */ class ActorManager { + /** + * Serializer for internal Dapr objects. + */ + private static final ObjectSerializer OBJECT_SERIALIZER = new ObjectSerializer(); + /** * Context for the Actor runtime. */ @@ -50,9 +53,8 @@ class ActorManager { * @return Asynchronous void response. */ Mono activateActor(ActorId actorId) { - T actor = this.runtimeContext.getActorFactory().createActor(runtimeContext, actorId); - - return actor.onActivateInternal().then(this.onActivatedActor(actorId, actor)); + return Mono.fromSupplier(() -> this.runtimeContext.getActorFactory().createActor(runtimeContext, actorId)) + .flatMap(actor -> actor.onActivateInternal().then(this.onActivatedActor(actorId, actor))); } /** @@ -62,12 +64,7 @@ Mono activateActor(ActorId actorId) { * @return Asynchronous void response. */ Mono deactivateActor(ActorId actorId) { - T actor = this.activeActors.remove(actorId); - if (actor != null) { - return actor.onDeactivateInternal(); - } - - return Mono.empty(); + return Mono.fromSupplier(() -> this.activeActors.remove(actorId)).flatMap(actor -> actor.onDeactivateInternal()); } /** @@ -78,7 +75,7 @@ Mono deactivateActor(ActorId actorId) { * @param request Input object for the method being invoked. * @return Asynchronous void response. */ - Mono invokeMethod(ActorId actorId, String methodName, String request) { + Mono invokeMethod(ActorId actorId, String methodName, byte[] request) { return invokeMethod(actorId, null, methodName, request); } @@ -90,24 +87,22 @@ Mono invokeMethod(ActorId actorId, String methodName, String request) { * @param params Parameters for the reminder. * @return Asynchronous void response. */ - Mono invokeReminder(ActorId actorId, String reminderName, String params) { - if (!this.runtimeContext.getActorTypeInformation().isRemindable()) { - return Mono.empty(); - } + Mono invokeReminder(ActorId actorId, String reminderName, byte[] params) { + return Mono.fromSupplier(() -> { + if (!this.runtimeContext.getActorTypeInformation().isRemindable()) { + return null; + } - try { - ActorReminderParams paramsObject = this - .runtimeContext - .getActorSerializer() - .deserialize(params, ActorReminderParams.class); - return invoke( + try { + return OBJECT_SERIALIZER.deserialize(params, ActorReminderParams.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).flatMap(p -> invoke( actorId, ActorMethodContext.CreateForReminder(reminderName), - actor -> doReminderInvokation((Remindable) actor, reminderName, paramsObject)) - .then(); - } catch (Exception e) { - return Mono.error(e); - } + actor -> doReminderInvokation((Remindable) actor, reminderName, p))) + .then(); } /** @@ -181,7 +176,7 @@ private Mono doReminderInvokation( return true; }).flatMap(x -> { try { - Object data = this.runtimeContext.getActorSerializer().deserialize( + Object data = this.runtimeContext.getObjectSerializer().deserialize( reminderParams.getData(), actor.getStateType()); return actor.receiveReminder( @@ -196,15 +191,15 @@ private Mono doReminderInvokation( } /** - * Internal method to actually invoke Actor's method. + * Internal method to actually invoke Actor's timer 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. + * @param input 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) { + private Mono invokeMethod(ActorId actorId, ActorMethodContext context, String methodName, Object input) { ActorMethodContext actorMethodContext = context; if (actorMethodContext == null) { actorMethodContext = ActorMethodContext.CreateForActor(methodName); @@ -216,60 +211,99 @@ private Mono invokeMethod(ActorId actorId, ActorMethodContext context, S Method method = this.actorMethods.get(methodName); if (method.getReturnType().equals(Mono.class)) { - Mono mono = (Mono) invokeMethod(actor, method, request); - if (mono == null) { - return Mono.just(new Object()); - } - - return mono.defaultIfEmpty("").map(r -> { - try { - return (Object) this.runtimeContext.getActorSerializer().serializeString(r); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + return invokeMonoMethod(actor, method, input); } + return invokeMethod(actor, method, input); + } catch (Exception e) { + return Mono.error(e); + } + }); + } + + /** + * 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 serialized response. + */ + private Mono invokeMethod(ActorId actorId, ActorMethodContext context, String methodName, byte[] request) { + ActorMethodContext actorMethodContext = context; + if (actorMethodContext == null) { + actorMethodContext = ActorMethodContext.CreateForActor(methodName); + } + + 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); - return Mono.fromSupplier(() -> { - try { - Object response = invokeMethod(actor, method, request); + Object input = null; + if (method.getParameterCount() == 1) { + // Actor methods must have a one or no parameter, which is guaranteed at this point. + Class inputClass = method.getParameterTypes()[0]; + input = this.runtimeContext.getObjectSerializer().deserialize(request, inputClass); + } - if (response == null) { - return new Object(); - } + if (method.getReturnType().equals(Mono.class)) { + return invokeMonoMethod(actor, method, input); + } - // Method was not Mono, so we serialize response. - return this.runtimeContext.getActorSerializer().serializeString(response); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); + return invokeMethod(actor, method, input); } catch (Exception e) { return Mono.error(e); } - }).map(r -> r.toString()); + }).map(r -> { + try { + return this.runtimeContext.getObjectSerializer().serialize(r); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } - private Object invokeMethod(AbstractActor actor, Method method, Object request) - throws IllegalAccessException, InvocationTargetException, IOException { - Object response; - 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 ((request != null) && !inputClass.isInstance(request)) { - // If request object is String, we deserialize it. - response = method.invoke( - actor, - this.runtimeContext.getActorSerializer().deserialize(request, inputClass)); + /** + * Invokes a method that returns Mono. + * @param actor Actor to be invoked. + * @param method Method to be invoked. + * @param input Input object for the method (or null). + * @return Asynchronous object response. + */ + private Mono invokeMonoMethod(AbstractActor actor, Method method, Object input) { + try { + if (method.getParameterCount() == 0) { + return (Mono) method.invoke(actor); } else { - // If input already of the right type, so we just cast it. - response = method.invoke(actor, inputClass.cast(request)); + // Actor methods must have a one or no parameter, which is guaranteed at this point. + return (Mono) method.invoke(actor, input); } + } catch (Exception e) { + return Mono.error(e); } - return response; + } + + /** + * Invokes a method that returns a plain object (not Mono). + * @param actor Actor to be invoked. + * @param method Method to be invoked. + * @param input Input object for the method (or null). + * @return Asynchronous object response. + */ + private Mono invokeMethod(AbstractActor actor, Method method, Object input) { + return Mono.fromSupplier(() -> { + try { + if (method.getParameterCount() == 0) { + return method.invoke(actor); + } else { + // Actor methods must have a one or no parameter, which is guaranteed at this point. + return method.invoke(actor, input); + } + } catch (Exception e) { + return Mono.error(e); + } + }); } /** @@ -292,12 +326,15 @@ private Mono invoke(ActorId actorId, ActorMethodContext context, Function } return actor.onPreActorMethodInternal(context) - .then(func.apply(actor)) - .flatMap(result -> actor.onPostActorMethodInternal(context).thenReturn(result)) + .then((Mono)func.apply(actor)) + .switchIfEmpty( + actor.onPostActorMethodInternal(context)) + .flatMap(r -> actor.onPostActorMethodInternal(context).thenReturn(r)) .onErrorMap(throwable -> { - actor.resetState(); + actor.rollback(); return throwable; - }); + }) + .map(o -> (T) o); } catch (Exception e) { return Mono.error(e); } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java index 96b7404021..a731bc2862 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorReminderParams.java @@ -20,7 +20,7 @@ final class ActorReminderParams { /** * Data to be passed in as part of the reminder trigger. */ - private final String data; + private final byte[] data; /** * Time the reminder is due for the 1st time. @@ -39,7 +39,7 @@ final class ActorReminderParams { * @param dueTime Time the reminder is due for the 1st time. * @param period Interval between triggers. */ - ActorReminderParams(String data, Duration dueTime, Duration period) { + ActorReminderParams(byte[] data, Duration dueTime, Duration period) { ValidateDueTime("DueTime", dueTime); ValidatePeriod("Period", period); this.data = data; @@ -70,7 +70,7 @@ Duration getPeriod() { * * @return Data to be passed in as part of the reminder trigger. */ - String getData() { + byte[] getData() { return data; } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntime.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntime.java index ad4f504938..20b37d851f 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntime.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntime.java @@ -6,8 +6,8 @@ import io.dapr.actors.ActorId; import io.dapr.actors.ActorTrace; -import io.dapr.client.DaprClient; -import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DaprHttpBuilder; +import io.dapr.client.DaprObjectSerializer; import reactor.core.publisher.Mono; import java.io.IOException; @@ -21,6 +21,11 @@ */ public class ActorRuntime { + /** + * Serializer for internal Dapr objects. + */ + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + /** * A trace type used when logging. */ @@ -46,16 +51,6 @@ public class ActorRuntime { */ private final DaprClient daprClient; - /** - * State provider for Dapr. - */ - private final DaprStateAsyncProvider daprStateProvider; - - /** - * Serializes/deserializes objects for Actors. - */ - private final ActorStateSerializer actorSerializer; - /** * Map of ActorType --> ActorManager. */ @@ -67,7 +62,7 @@ public class ActorRuntime { * @throws IllegalStateException */ private ActorRuntime() throws IllegalStateException { - this(new DaprClientBuilder().build()); + this(new DaprHttpClient(new DaprHttpBuilder().build())); } /** @@ -84,8 +79,6 @@ private ActorRuntime(DaprClient daprClient) throws IllegalStateException { this.config = new ActorRuntimeConfig(); this.actorManagers = Collections.synchronizedMap(new HashMap<>()); this.daprClient = daprClient; - this.actorSerializer = new ActorStateSerializer(); - this.daprStateProvider = new DaprStateAsyncProvider(this.daprClient, this.actorSerializer); } /** @@ -108,43 +101,52 @@ public static ActorRuntime getInstance() { /** * Gets the Actor configuration for this runtime. * - * @return Actor configuration serialized in a String. + * @return Actor configuration serialized. * @throws IOException If cannot serialize config. */ - public String serializeConfig() throws IOException { - return this.actorSerializer.serializeString(this.config); + public byte[] serializeConfig() throws IOException { + return this.INTERNAL_SERIALIZER.serialize(this.config); } /** * Registers an actor with the runtime. * - * @param clazz The type of actor. - * @param Actor class type. + * @param clazz The type of actor. + * @param objectSerializer Serializer for Actor's state and transient objects. + * @param Actor class type. */ - public void registerActor(Class clazz) { - registerActor(clazz, null); + public void registerActor(Class clazz, DaprObjectSerializer objectSerializer) { + registerActor(clazz, null, objectSerializer); } /** * Registers an actor with the runtime. * - * @param clazz The type of actor. - * @param actorFactory An optional factory to create actors. - * @param Actor class type. - * This can be used for dependency injection into actors. + * @param clazz The type of actor. + * @param actorFactory An optional factory to create actors. This can be used for dependency injection. + * @param serializer Serializer for Actor's state and transient objects. + * @param Actor class type. */ - public void registerActor(Class clazz, ActorFactory actorFactory) { + public void registerActor( + Class clazz, ActorFactory actorFactory, DaprObjectSerializer serializer) { + if (clazz == null) { + throw new IllegalArgumentException("Class is required."); + } + if (serializer == null) { + throw new IllegalArgumentException("Serializer is required."); + } + ActorTypeInformation actorTypeInfo = ActorTypeInformation.create(clazz); ActorFactory actualActorFactory = actorFactory != null ? actorFactory : new DefaultActorFactory(); - ActorRuntimeContext context = new ActorRuntimeContext( + ActorRuntimeContext context = new ActorRuntimeContext<>( this, - this.actorSerializer, + serializer, actualActorFactory, actorTypeInfo, this.daprClient, - this.daprStateProvider); + new DaprStateAsyncProvider(this.daprClient, serializer)); // Create ActorManagers, override existing entry if registered again. this.actorManagers.put(actorTypeInfo.getName(), new ActorManager(context)); @@ -159,7 +161,8 @@ public void registerActor(Class clazz, ActorFactory * @return Async void task. */ public Mono activate(String actorTypeName, String actorId) { - return Mono.defer(() -> this.getActorManager(actorTypeName).activateActor(new ActorId(actorId))); + return Mono.fromSupplier(() -> this.getActorManager(actorTypeName)) + .flatMap(m -> m.activateActor(new ActorId(actorId))); } /** @@ -170,7 +173,8 @@ public Mono activate(String actorTypeName, String actorId) { * @return Async void task. */ public Mono deactivate(String actorTypeName, String actorId) { - return Mono.defer(() -> this.getActorManager(actorTypeName).deactivateActor(new ActorId(actorId))); + return Mono.fromSupplier(() -> this.getActorManager(actorTypeName)) + .flatMap(m -> m.deactivateActor(new ActorId(actorId))); } /** @@ -183,10 +187,10 @@ public Mono deactivate(String actorTypeName, String actorId) { * @param payload RAW payload for the actor method. * @return Response for the actor method. */ - public Mono invoke(String actorTypeName, String actorId, String actorMethodName, String payload) { - return Mono.defer(() -> - this.getActorManager(actorTypeName).invokeMethod(new ActorId(actorId), actorMethodName, unwrap(payload))) - .map(response -> wrap(response.toString())); + public Mono invoke(String actorTypeName, String actorId, String actorMethodName, byte[] payload) { + return Mono.fromSupplier(() -> this.getActorManager(actorTypeName)) + .flatMap(m -> m.invokeMethod(new ActorId(actorId), actorMethodName, unwrap(payload))) + .map(response -> wrap((byte[]) response)); } /** @@ -198,9 +202,9 @@ public Mono invoke(String actorTypeName, String actorId, String actorMet * @param params Params for the reminder. * @return Async void task. */ - public Mono invokeReminder(String actorTypeName, String actorId, String reminderName, String params) { - return Mono.defer(() -> - this.getActorManager(actorTypeName).invokeReminder(new ActorId(actorId), reminderName, params)); + public Mono invokeReminder(String actorTypeName, String actorId, String reminderName, byte[] params) { + return Mono.fromSupplier(() -> this.getActorManager(actorTypeName)) + .flatMap(m -> m.invokeReminder(new ActorId(actorId), reminderName, params)); } /** @@ -212,7 +216,8 @@ public Mono invokeReminder(String actorTypeName, String actorId, String re * @return Async void task. */ public Mono invokeTimer(String actorTypeName, String actorId, String timerName) { - return Mono.defer(() -> this.getActorManager(actorTypeName).invokeTimer(new ActorId(actorId), timerName)); + return Mono.fromSupplier(() -> this.getActorManager(actorTypeName)) + .flatMap(m -> m.invokeTimer(new ActorId(actorId), timerName)); } /** @@ -238,17 +243,12 @@ private ActorManager getActorManager(String actorTypeName) { * Extracts the data as String from the Actor's method result. * * @param payload String returned by API. - * @return String or null. - * @throws RuntimeException In case it cannot generate String. + * @return data or null. + * @throws RuntimeException In case it cannot extract data. */ - private String unwrap(final String payload) { + private byte[] unwrap(final byte[] payload) { try { - byte[] data = this.actorSerializer.unwrapData(payload); - if (data == null) { - return null; - } - - return new String(data); + return INTERNAL_SERIALIZER.unwrapData(payload); } catch (IOException e) { throw new RuntimeException(e); } @@ -257,17 +257,13 @@ private String unwrap(final String payload) { /** * Builds the request to invoke an API for Actors. * - * @param payload String to be wrapped in the request. - * @return String to be sent to Dapr's API. - * @throws RuntimeException In case it cannot generate String. + * @param data Data to be wrapped in the request. + * @return Payload to be sent to Dapr's API. + * @throws RuntimeException In case it cannot generate payload. */ - private String wrap(final String payload) { + private byte[] wrap(final byte[] data) { try { - byte[] data = null; - if (payload != null) { - data = payload.getBytes(); - } - return this.actorSerializer.wrapData(data); + return INTERNAL_SERIALIZER.wrapData(data); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java index fd2eb15682..be8e3e4136 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorRuntimeContext.java @@ -5,8 +5,8 @@ package io.dapr.actors.runtime; +import io.dapr.client.DaprObjectSerializer; import io.dapr.actors.ActorTrace; -import io.dapr.client.DaprClient; /** * Provides the context for the Actor's runtime. @@ -15,126 +15,126 @@ */ 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 DaprClient 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, - DaprClient 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. - */ - DaprClient 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 DaprObjectSerializer objectSerializer; + + /** + * 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 DaprClient daprClient; + + /** + * State provider for given Actor Type. + */ + private final DaprStateAsyncProvider stateProvider; + + /** + * Instantiates a new runtime context for the Actor type. + * + * @param actorRuntime Runtime. + * @param objectSerializer 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, + DaprObjectSerializer objectSerializer, + ActorFactory actorFactory, + ActorTypeInformation actorTypeInformation, + DaprClient daprClient, + DaprStateAsyncProvider stateProvider) { + this.actorRuntime = actorRuntime; + this.objectSerializer = objectSerializer; + 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. + */ + DaprObjectSerializer getObjectSerializer() { + return this.objectSerializer; + } + + /** + * 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. + */ + DaprClient 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-actors/src/main/java/io/dapr/actors/runtime/ActorStateManager.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateManager.java index 143c325dbf..d8677da756 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateManager.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateManager.java @@ -118,7 +118,7 @@ public Mono get(String stateName, Class clazz) { 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)); + this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.NONE, v)); return (T) v; })); } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorType.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorType.java index 3c4403cc94..3e56bb15f5 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorType.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorType.java @@ -7,7 +7,7 @@ import java.lang.annotation.*; /** - * Annotation to override default behavior of Actor class. + * Annotation to define Actor class. */ @Documented @Target(ElementType.TYPE_USE) @@ -19,6 +19,6 @@ * * @return Actor's name. */ - String Name(); + String name(); } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java index 604cf43305..de635425ac 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeInformation.java @@ -79,8 +79,7 @@ public Class getImplementationClass() { } /** - * Gets the actor interfaces which derive from {@link Actor} and implemented - * by actor class. + * Gets the actor interfaces that are implemented by actor class. * * @return Collection of actor interfaces. */ @@ -133,31 +132,18 @@ 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'.", + "The type '%s' is not an Actor. An actor class must inherit from '%s'.", actorClass == null ? "" : actorClass.getCanonicalName(), - Actor.class.getCanonicalName())); + AbstractActor.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(); + ActorType actorTypeAnnotation = 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-actors/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java index 0eba233ade..b436b56a05 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorTypeUtilities.java @@ -23,7 +23,7 @@ public static Class[] getActorInterfaces(Class clazz) { } return Arrays.stream(clazz.getInterfaces()) - .filter(t -> Actor.class.isAssignableFrom(t)) + .filter(t -> AbstractActor.class.isAssignableFrom(t)) .filter(t -> getNonActorParentClass(t) == null) .toArray(Class[]::new); } @@ -74,7 +74,9 @@ public static Class getNonActorParentClass(Class clazz) { return null; } - Class[] items = Arrays.stream(clazz.getInterfaces()).filter(t -> !t.equals(Actor.class)).toArray(Class[]::new); + Class[] items = Arrays.stream(clazz.getInterfaces()) + .filter(t -> !t.equals(AbstractActor.class)) + .toArray(Class[]::new); if (items.length == 0) { return clazz; } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprClient.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprClient.java new file mode 100644 index 0000000000..d5edb242eb --- /dev/null +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprClient.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.runtime; + +import reactor.core.publisher.Mono; + +/** + * Generic Client Adapter to be used regardless of the GRPC or the HTTP Client implementation required. + */ +interface DaprClient { + + /** + * 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 getActorState(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 saveActorStateTransactionally(String actorType, String actorId, byte[] 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 registerActorReminder(String actorType, String actorId, String reminderName, byte[] 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 unregisterActorReminder(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 registerActorTimer(String actorType, String actorId, String timerName, byte[] 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 unregisterActorTimer(String actorType, String actorId, String timerName); +} diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprHttpClient.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprHttpClient.java new file mode 100644 index 0000000000..351c1ec15d --- /dev/null +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprHttpClient.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.runtime; + +import io.dapr.client.DaprHttp; +import io.dapr.utils.Constants; +import reactor.core.publisher.Mono; + +/** + * A DaprClient over HTTP for Actor's runtime. + */ +class DaprHttpClient implements DaprClient { + + /** + * The HTTP client to be used + * + * @see DaprHttp + */ + private final DaprHttp client; + + /** + * Internal constructor. + * + * @param client Dapr's http client. + */ + DaprHttpClient(DaprHttp client) { + this.client = client; + } + + /** + * {@inheritDoc} + */ + @Override + public Mono getActorState(String actorType, String actorId, String keyName) { + String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); + Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, null, "", null); + return responseMono.map(r -> r.getBody()); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono saveActorStateTransactionally(String actorType, String actorId, byte[] data) { + String url = String.format(Constants.ACTOR_STATE_RELATIVE_URL_FORMAT, actorType, actorId); + return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url, null, data, null).then(); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono registerActorReminder(String actorType, String actorId, String reminderName, byte[] data) { + String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); + return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url, null, data, null).then(); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono unregisterActorReminder(String actorType, String actorId, String reminderName) { + String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); + return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, null, null).then(); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono registerActorTimer(String actorType, String actorId, String timerName, byte[] data) { + String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); + return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url,null, data, null).then(); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono unregisterActorTimer(String actorType, String actorId, String timerName) { + String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); + return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, null, null).then(); + } + +} diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java index 4965855124..8250b65b66 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/DaprStateAsyncProvider.java @@ -8,12 +8,11 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; +import io.dapr.client.DaprObjectSerializer; import reactor.core.publisher.Mono; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; /** * State Provider to interact with Dapr runtime to handle state. @@ -27,33 +26,33 @@ class DaprStateAsyncProvider { private final DaprClient daprClient; - private final ActorStateSerializer serializer; + private final DaprObjectSerializer serializer; - DaprStateAsyncProvider(DaprClient daprClient, ActorStateSerializer serializer) { + DaprStateAsyncProvider(DaprClient daprClient, DaprObjectSerializer serializer) { this.daprClient = daprClient; this.serializer = serializer; } Mono load(String actorType, ActorId actorId, String stateName, Class clazz) { - Mono result = this.daprClient.getActorState(actorType, actorId.toString(), stateName); + Mono result = this.daprClient.getActorState(actorType, actorId.toString(), stateName); - return result - .filter(s -> (s != null) && (!s.isEmpty())) - .map(s -> { + return result.flatMap(s -> { try { - return this.serializer.deserialize(s, clazz); + T response = this.serializer.deserialize(s, clazz); + if (response == null) { + return Mono.empty(); + } + + return Mono.just(response); } catch (IOException e) { - throw new RuntimeException(e); + return Mono.error(new RuntimeException(e)); } }); } Mono contains(String actorType, ActorId actorId, String stateName) { - Mono result = this.daprClient.getActorState(actorType, actorId.toString(), stateName); - - return result.map(s -> { - return (s != null) && (s.length() > 0); - }); + Mono result = this.daprClient.getActorState(actorType, actorId.toString(), stateName); + return result.map(s -> true).defaultIfEmpty(false); } /** @@ -86,8 +85,8 @@ Mono apply(String actorType, ActorId actorId, ActorStateChange... stateCha 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()) { + byte[] payload = null; + try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { JsonGenerator generator = JSON_FACTORY.createGenerator(writer); // Start array generator.writeStartArray(); @@ -112,7 +111,7 @@ Mono apply(String actorType, ActorId actorId, ActorStateChange... stateCha generator.writeObjectFieldStart("request"); generator.writeStringField("key", stateChange.getStateName()); if ((stateChange.getChangeKind() == ActorStateChangeKind.UPDATE) || (stateChange.getChangeKind() == ActorStateChangeKind.ADD)) { - generator.writeStringField("value", this.serializer.serializeString(stateChange.getValue())); + generator.writeBinaryField("value", this.serializer.serialize(stateChange.getValue())); } // End request object. generator.writeEndObject(); @@ -126,7 +125,7 @@ Mono apply(String actorType, ActorId actorId, ActorStateChange... stateCha generator.close(); writer.flush(); - payload = writer.toString(); + payload = writer.toByteArray(); } catch (IOException e) { e.printStackTrace(); return Mono.error(e); diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java index b6ab5d797a..83113df35f 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/DefaultActorFactory.java @@ -6,16 +6,22 @@ package io.dapr.actors.runtime; import io.dapr.actors.ActorId; +import io.dapr.actors.ActorTrace; import java.lang.reflect.Constructor; /** - * Instantiates actors by calling their constructor with {@link ActorService} and {@link ActorId}. + * Instantiates actors by calling their constructor with {@link ActorRuntimeContext} and {@link ActorId}. * * @param Actor Type to be created. */ class DefaultActorFactory implements ActorFactory { + /** + * Tracing errors, warnings and info logs. + */ + private static final ActorTrace ACTOR_TRACE = new ActorTrace(); + /** * {@inheritDoc} */ @@ -32,8 +38,10 @@ public T createActor(ActorRuntimeContext actorRuntimeContext, ActorId actorId .getConstructor(ActorRuntimeContext.class, ActorId.class); return constructor.newInstance(actorRuntimeContext, actorId); } catch (Exception e) { - //TODO: Use ActorTrace. - e.printStackTrace(); + ACTOR_TRACE.writeError( + actorRuntimeContext.getActorTypeInformation().getName(), + actorId.toString(), + "Failed to create actor instance."); } return null; } diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ObjectSerializer.java similarity index 72% rename from sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java rename to sdk-actors/src/main/java/io/dapr/actors/runtime/ObjectSerializer.java index 47d0ededd7..7581f911a5 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ObjectSerializer.java @@ -4,26 +4,30 @@ */ package io.dapr.actors.runtime; +import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonNode; import io.dapr.utils.DurationUtils; -import io.dapr.utils.ObjectSerializer; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; import java.time.Duration; /** - * Serializes and deserializes an object. + * Serializes and deserializes internal objects. */ -public class ActorStateSerializer extends ObjectSerializer { +public class ObjectSerializer extends io.dapr.client.ObjectSerializer { + + /** + * Shared Json Factory as per Jackson's documentation. + */ + private static final JsonFactory JSON_FACTORY = new JsonFactory(); /** * {@inheritDoc} */ @Override - public String serializeString(T state) throws IOException { + public byte[] serialize(Object state) throws IOException { if (state == null) { return null; } @@ -44,21 +48,21 @@ public String serializeString(T state) throws IOException { } // Is not an special case. - return super.serializeString(state); + return super.serialize(state); } /** * {@inheritDoc} */ @Override - public T deserialize(Object value, Class clazz) throws IOException { + public T deserialize(byte[] content, Class clazz) throws IOException { if (clazz == ActorReminderParams.class) { // Special serializer for this internal classes. - return (T) deserializeActorReminder(value); + return (T) deserializeActorReminder(content); } // Is not one of the special cases. - return super.deserialize(value, clazz); + return super.deserialize(content, clazz); } /** @@ -68,7 +72,7 @@ public T deserialize(Object value, Class clazz) throws IOException { * @return byte[] instance, null. * @throws IOException In case it cannot generate String. */ - public byte[] unwrapData(final String payload) throws IOException { + public byte[] unwrapData(final byte[] payload) throws IOException { if (payload == null) { return null; } @@ -93,17 +97,17 @@ public byte[] unwrapData(final String payload) throws IOException { * @return String to be sent to Dapr's API. * @throws RuntimeException In case it cannot generate String. */ - public String wrapData(final byte[] data) throws IOException { - try (Writer writer = new StringWriter()) { - JsonGenerator generator = JSON_FACTORY.createGenerator(writer); + public byte[] wrapData(final byte[] data) throws IOException { + try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + JsonGenerator generator = JSON_FACTORY.createGenerator(output); generator.writeStartObject(); if (data != null) { generator.writeBinaryField("data", data); } generator.writeEndObject(); generator.close(); - writer.flush(); - return writer.toString(); + output.flush(); + return output.toByteArray(); } } @@ -114,24 +118,24 @@ public String wrapData(final byte[] data) throws IOException { * @return JSON String. * @throws IOException If cannot generate JSON. */ - private String serialize(ActorTimer timer) throws IOException { + private byte[] serialize(ActorTimer timer) throws IOException { if (timer == null) { return null; } - try (Writer writer = new StringWriter()) { + try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { 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.serializeString(timer.getState())); + generator.writeBinaryField("data", this.serialize(timer.getState())); } generator.writeEndObject(); generator.close(); writer.flush(); - return writer.toString(); + return writer.toByteArray(); } } @@ -142,19 +146,19 @@ private String serialize(ActorTimer timer) throws IOException { * @return JSON String. * @throws IOException If cannot generate JSON. */ - private String serialize(ActorReminderParams reminder) throws IOException { - try (Writer writer = new StringWriter()) { + private byte[] serialize(ActorReminderParams reminder) throws IOException { + try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { 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.writeBinaryField("data", reminder.getData()); } generator.writeEndObject(); generator.close(); writer.flush(); - return writer.toString(); + return writer.toByteArray(); } } @@ -165,8 +169,8 @@ private String serialize(ActorReminderParams reminder) throws IOException { * @return JSON String. * @throws IOException If cannot generate JSON. */ - private String serialize(ActorRuntimeConfig config) throws IOException { - try (Writer writer = new StringWriter()) { + private byte[] serialize(ActorRuntimeConfig config) throws IOException { + try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { JsonGenerator generator = JSON_FACTORY.createGenerator(writer); generator.writeStartObject(); generator.writeArrayFieldStart("entities"); @@ -178,7 +182,7 @@ private String serialize(ActorRuntimeConfig config) throws IOException { generator.writeEndObject(); generator.close(); writer.flush(); - return writer.toString(); + return writer.toByteArray(); } } @@ -189,20 +193,15 @@ private String serialize(ActorRuntimeConfig config) throws IOException { * @return Actor Reminder. * @throws IOException If cannot parse JSON. */ - private ActorReminderParams deserializeActorReminder(Object value) throws IOException { + private ActorReminderParams deserializeActorReminder(byte[] 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()); - } + 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; + byte[] data = node.get("data") != null ? node.get("data").binaryValue() : null; return new ActorReminderParams(data, dueTime, period); } diff --git a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyBuilderTest.java b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyBuilderTest.java index aa17d884ce..8902b2eac7 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyBuilderTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyBuilderTest.java @@ -1,6 +1,7 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; @@ -10,37 +11,36 @@ public class ActorProxyBuilderTest { @Test(expected = IllegalArgumentException.class) public void buildWithNullActorId() { - new ActorProxyBuilder() - .withActorId(null) - .withActorType("test") - .build(); + new ActorProxyBuilder("test", new DefaultObjectSerializer()) + .build(null); } @Test(expected = IllegalArgumentException.class) public void buildWithEmptyActorType() { - new ActorProxyBuilder() - .withActorId(new ActorId("100")) - .withActorType("") - .build(); + new ActorProxyBuilder("", new DefaultObjectSerializer()) + .build(new ActorId("100")); } @Test(expected = IllegalArgumentException.class) public void buildWithNullActorType() { - new ActorProxyBuilder() - .withActorId(new ActorId("100")) - .withActorType(null) - .build(); + new ActorProxyBuilder(null, new DefaultObjectSerializer()) + .build(new ActorId("100")); + + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithNullSerializer() { + new ActorProxyBuilder("MyActor", null) + .build(new ActorId("100")); } @Test() public void build() { - ActorProxyBuilder builder = new ActorProxyBuilder(); - builder.withActorId(new ActorId("100")); - builder.withActorType("test"); - ActorProxy actorProxy = builder.build(); + ActorProxyBuilder builder = new ActorProxyBuilder("test", new DefaultObjectSerializer()); + ActorProxy actorProxy = builder.build(new ActorId("100")); Assert.assertNotNull(actorProxy); Assert.assertEquals("test", actorProxy.getActorType()); diff --git a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyForTestsImpl.java b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyForTestsImpl.java index df0006841b..b3dbfec96b 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyForTestsImpl.java +++ b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyForTestsImpl.java @@ -6,12 +6,11 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.ActorStateSerializer; -import io.dapr.client.DaprClient; +import io.dapr.client.DaprObjectSerializer; public class ActorProxyForTestsImpl extends ActorProxyImpl { - public ActorProxyForTestsImpl(String actorType, ActorId actorId, ActorStateSerializer serializer, DaprClient daprClient) { + public ActorProxyForTestsImpl(String actorType, ActorId actorId, DaprObjectSerializer serializer, DaprClient daprClient) { super(actorType, actorId, serializer, daprClient); } } diff --git a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java index b81d2f5e60..18a3746ffb 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/client/ActorProxyImplTest.java @@ -1,8 +1,8 @@ package io.dapr.actors.client; import io.dapr.actors.ActorId; -import io.dapr.actors.runtime.ActorStateSerializer; -import io.dapr.client.DaprClient; +import io.dapr.client.DefaultObjectSerializer; +import io.dapr.client.DaprObjectSerializer; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -17,7 +17,7 @@ public class ActorProxyImplTest { @Test() public void constructorActorProxyTest() { final DaprClient daprClient = mock(DaprClient.class); - final ActorStateSerializer serializer = mock(ActorStateSerializer.class); + final DaprObjectSerializer serializer = mock(DaprObjectSerializer.class); final ActorProxyImpl actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), @@ -30,15 +30,16 @@ public void constructorActorProxyTest() { @Test() public void invokeActorMethodWithoutDataWithReturnType() { final DaprClient daprClient = mock(DaprClient.class); + Mono daprResponse = + Mono.just("{\n\t\"data\": \"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"\n}" + .getBytes()); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNull())) - .thenReturn(Mono.just("{\n" + - "\t\"data\": \"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"\n" + - "}")); + .thenReturn(daprResponse); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData", MyData.class); @@ -52,12 +53,12 @@ public void invokeActorMethodWithoutDataWithReturnType() { public void invokeActorMethodWithoutDataWithEmptyReturnType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNull())) - .thenReturn(Mono.just("")); + .thenReturn(Mono.just("".getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData", MyData.class); @@ -69,12 +70,12 @@ public void invokeActorMethodWithoutDataWithEmptyReturnType() { public void invokeActorMethodWithIncorrectReturnType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNull())) - .thenReturn(Mono.just("{test}")); + .thenReturn(Mono.just("{test}".getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData", MyData.class); @@ -91,14 +92,14 @@ public void invokeActorMethodWithIncorrectReturnType() { public void invokeActorMethodSavingDataWithReturnType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNotNull())) - .thenReturn(Mono.just("{\n" + - "\t\"data\": \"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"\n" + - "}")); + .thenReturn( + Mono.just("{\n\t\"data\": \"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"\n}" + .getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); MyData saveData = new MyData(); @@ -117,12 +118,12 @@ public void invokeActorMethodSavingDataWithReturnType() { public void invokeActorMethodSavingDataWithIncorrectReturnType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNotNull())) - .thenReturn(Mono.just("{test}")); + .thenReturn(Mono.just("{test}".getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); MyData saveData = new MyData(); @@ -141,12 +142,12 @@ public void invokeActorMethodSavingDataWithIncorrectReturnType() { public void invokeActorMethodSavingDataWithEmptyReturnType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNotNull())) - .thenReturn(Mono.just("")); + .thenReturn(Mono.just("".getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); MyData saveData = new MyData(); @@ -163,12 +164,12 @@ public void invokeActorMethodSavingDataWithEmptyReturnType() { public void invokeActorMethodSavingDataWithIncorrectInputType() { final DaprClient daprClient = mock(DaprClient.class); when(daprClient.invokeActorMethod(anyString(), anyString(), anyString(), Mockito.isNotNull())) - .thenReturn(Mono.just("{test}")); + .thenReturn(Mono.just("{test}".getBytes())); final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); MyData saveData = new MyData(); @@ -197,7 +198,7 @@ public void invokeActorMethodWithDataWithVoidReturnType() { final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData", saveData); @@ -220,7 +221,7 @@ public void invokeActorMethodWithDataWithVoidIncorrectInputType() { final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData", saveData); @@ -237,7 +238,7 @@ public void invokeActorMethodWithoutDataWithVoidReturnType() { final ActorProxy actorProxy = new ActorProxyImpl( "myActorType", new ActorId("100"), - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); Mono result = actorProxy.invokeActorMethod("getData"); diff --git a/sdk-actors/src/test/java/io/dapr/actors/client/DaprClientStub.java b/sdk-actors/src/test/java/io/dapr/actors/client/DaprClientStub.java new file mode 100644 index 0000000000..374b61a316 --- /dev/null +++ b/sdk-actors/src/test/java/io/dapr/actors/client/DaprClientStub.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.actors.client; + +import reactor.core.publisher.Mono; + +public class DaprClientStub implements DaprClient { + + @Override + public Mono invokeActorMethod(String actorType, String actorId, String methodName, byte[] jsonPayload) { + return Mono.just(new byte[0]); + } + +} diff --git a/sdk-actors/src/test/java/io/dapr/actors/client/DaprHttpClientTest.java b/sdk-actors/src/test/java/io/dapr/actors/client/DaprHttpClientTest.java new file mode 100644 index 0000000000..553b15d3e7 --- /dev/null +++ b/sdk-actors/src/test/java/io/dapr/actors/client/DaprHttpClientTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.client; + +import io.dapr.client.DaprHttp; +import io.dapr.client.DaprHttpProxy; +import okhttp3.OkHttpClient; +import okhttp3.mock.Behavior; +import okhttp3.mock.MockInterceptor; +import org.junit.Before; +import org.junit.Test; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +public class DaprHttpClientTest { + + private DaprHttpClient DaprHttpClient; + + private OkHttpClient okHttpClient; + + private MockInterceptor mockInterceptor; + + private final String EXPECTED_RESULT = "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; + + @Before + public void setUp() { + mockInterceptor = new MockInterceptor(Behavior.UNORDERED); + okHttpClient = new OkHttpClient.Builder().addInterceptor(mockInterceptor).build(); + } + + @Test + public void invokeActorMethod() { + DaprHttp daprHttpMock = mock(DaprHttp.class); + mockInterceptor.addRule() + .post("http://localhost:3000/v1.0/actors/DemoActor/1/method/Payment") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = + DaprHttpClient.invokeActorMethod("DemoActor", "1", "Payment", "".getBytes()); + assertEquals(new String(mono.block()), EXPECTED_RESULT); + } + +} \ No newline at end of file diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java index f79dfca8a3..76a22387b5 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorManagerTest.java @@ -6,7 +6,7 @@ package io.dapr.actors.runtime; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; @@ -24,6 +24,8 @@ */ public class ActorManagerTest { + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); interface MyActor { @@ -34,14 +36,14 @@ interface MyActor { void incrementCount(int delta); } - public static class NotRemindableActor extends AbstractActor implements Actor { + public static class NotRemindableActor extends AbstractActor { public NotRemindableActor(ActorRuntimeContext runtimeContext, ActorId id) { super(runtimeContext, id); } } - @ActorType(Name = "MyActor") - public static class MyActorImpl extends AbstractActor implements Actor, MyActor, Remindable { + @ActorType(name = "MyActor") + public static class MyActorImpl extends AbstractActor implements MyActor, Remindable { private int timeCount = 0; @@ -90,25 +92,29 @@ public Mono receiveReminder(String reminderName, String state, Duration du public void invokeBeforeActivate() throws Exception { ActorId actorId = newActorId(); String message = "something"; - this.manager.invokeMethod(actorId, "say", message).block(); + this.manager.invokeMethod(actorId, "say", message.getBytes()).block(); } @Test - public void activateThenInvoke() { + public void activateThenInvoke() throws Exception { ActorId actorId = newActorId(); - String message = "something"; + byte[] message = this.context.getObjectSerializer().serialize("something"); this.manager.activateActor(actorId).block(); - String response = this.manager.invokeMethod(actorId, "say", message).block(); - Assert.assertEquals(executeSayMethod(message), response); + byte[] response = this.manager.invokeMethod(actorId, "say", message).block(); + Assert.assertEquals(executeSayMethod( + this.context.getObjectSerializer().deserialize(message, String.class)), + this.context.getObjectSerializer().deserialize(response, String.class)); } @Test(expected = IllegalArgumentException.class) - public void activateInvokeDeactivateThenInvoke() { + public void activateInvokeDeactivateThenInvoke() throws Exception { ActorId actorId = newActorId(); - String message = "something"; + byte[] message = this.context.getObjectSerializer().serialize("something"); this.manager.activateActor(actorId).block(); - String response = this.manager.invokeMethod(actorId, "say", message).block(); - Assert.assertEquals(executeSayMethod(message), response); + byte[] response = this.manager.invokeMethod(actorId, "say", message).block(); + Assert.assertEquals(executeSayMethod( + this.context.getObjectSerializer().deserialize(message, String.class)), + this.context.getObjectSerializer().deserialize(response, String.class)); this.manager.deactivateActor(actorId).block(); this.manager.invokeMethod(actorId, "say", message).block(); @@ -161,8 +167,8 @@ 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); + byte[] response = this.manager.invokeMethod(actorId, "getCount", null).block(); + Assert.assertEquals("2", new String(response)); } @Test(expected = IllegalArgumentException.class) @@ -170,16 +176,17 @@ 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); + byte[] response = this.manager.invokeMethod(actorId, "getCount", null).block(); + Assert.assertEquals("2", new String(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().serializeString(params); + private byte[] createReminderParams(String data) throws IOException { + byte[] serializedData = this.context.getObjectSerializer().serialize(data); + ActorReminderParams params = new ActorReminderParams(serializedData, Duration.ofSeconds(1), Duration.ofSeconds(1)); + return INTERNAL_SERIALIZER.serialize(params); } private static ActorId newActorId() { @@ -200,7 +207,7 @@ private static ActorRuntimeContext createContext(Class return new ActorRuntimeContext( mock(ActorRuntime.class), - new ActorStateSerializer(), + new DefaultObjectSerializer(), new DefaultActorFactory(), ActorTypeInformation.create(clazz), daprClient, diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorMethodInfoMapTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorMethodInfoMapTest.java index fbaba3df73..bcd888de40 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorMethodInfoMapTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorMethodInfoMapTest.java @@ -47,7 +47,7 @@ public void lookUpNonExistingMethod() throws NoSuchMethodException { /** * Only used for this test. */ - public interface TestActor extends Actor { + public interface TestActor { String getData(String key); } } diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorNoStateTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorNoStateTest.java index 47b1d0e6c2..07cffa327c 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorNoStateTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorNoStateTest.java @@ -8,18 +8,12 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyForTestsImpl; -import io.dapr.client.DaprClient; +import io.dapr.actors.client.DaprClientStub; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; -import java.io.IOException; -import java.io.NotSerializableException; -import java.nio.charset.IllegalCharsetNameException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import static org.mockito.ArgumentMatchers.any; @@ -28,9 +22,12 @@ import static org.mockito.Mockito.when; public class ActorNoStateTest { + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); private final ActorRuntimeContext context = createContext(); + private ActorManager manager = new ActorManager<>(context); public interface MyActor { @@ -42,8 +39,8 @@ public interface MyActor { Mono classInClassOut(MyData input); } - @ActorType(Name = "MyActor") - public static class ActorImpl extends AbstractActor implements MyActor, Actor { + @ActorType(name = "MyActor") + public static class ActorImpl extends AbstractActor implements MyActor { private final ActorId id; private boolean activated; private boolean methodReturningVoidInvoked; @@ -173,7 +170,7 @@ private ActorProxy createActorProxy() { ActorId actorId = newActorId(); // Mock daprClient for ActorProxy only, not for runtime. - DaprClient daprClient = mock(DaprClient.class); + DaprClientStub daprClient = mock(DaprClientStub.class); when(daprClient.invokeActorMethod( eq(context.getActorTypeInformation().getName()), @@ -184,11 +181,10 @@ private ActorProxy createActorProxy() { this.manager.invokeMethod( new ActorId(invocationOnMock.getArgument(1, String.class)), invocationOnMock.getArgument(2, String.class), - Utilities.toStringOrNull(context.getActorSerializer().unwrapData( - invocationOnMock.getArgument(3, String.class)))) + INTERNAL_SERIALIZER.unwrapData(invocationOnMock.getArgument(3, byte[].class))) .map(s -> { try { - return context.getActorSerializer().wrapData(s.getBytes()); + return INTERNAL_SERIALIZER.wrapData(s); } catch (Exception e) { throw new RuntimeException(e); } @@ -199,7 +195,7 @@ private ActorProxy createActorProxy() { return new ActorProxyForTestsImpl( context.getActorTypeInformation().getName(), actorId, - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); } @@ -213,7 +209,7 @@ private static ActorRuntimeContext createContext() { return new ActorRuntimeContext( mock(ActorRuntime.class), - new ActorStateSerializer(), + new DefaultObjectSerializer(), new DefaultActorFactory(), ActorTypeInformation.create(ActorImpl.class), daprClient, diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorReminderParamsTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorReminderParamsTest.java index 2e30825a6a..ebdf12407d 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorReminderParamsTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorReminderParamsTest.java @@ -7,7 +7,7 @@ public class ActorReminderParamsTest { - private static final ActorStateSerializer SERIALIZER = new ActorStateSerializer(); + private static final ObjectSerializer SERIALIZER = new ObjectSerializer(); @Test(expected = IllegalArgumentException.class) public void outOfRangeDueTime() { @@ -30,7 +30,7 @@ public void noState() { ActorReminderParams original = new ActorReminderParams(null, Duration.ZERO.plusMinutes(2), Duration.ZERO.plusMinutes((5))); ActorReminderParams recreated = null; try { - String serialized = SERIALIZER.serializeString(original); + byte[] serialized = SERIALIZER.serialize(original); recreated = SERIALIZER.deserialize(serialized, ActorReminderParams.class); } catch(Exception e) { @@ -38,17 +38,17 @@ public void noState() { Assert.fail(); } - Assert.assertEquals(original.getData(), recreated.getData()); + Assert.assertArrayEquals(original.getData(), recreated.getData()); Assert.assertEquals(original.getDueTime(), recreated.getDueTime()); Assert.assertEquals(original.getPeriod(), recreated.getPeriod()); } @Test public void withState() { - ActorReminderParams original = new ActorReminderParams("maru", Duration.ZERO.plusMinutes(2), Duration.ZERO.plusMinutes((5))); + ActorReminderParams original = new ActorReminderParams("maru".getBytes(), Duration.ZERO.plusMinutes(2), Duration.ZERO.plusMinutes((5))); ActorReminderParams recreated = null; try { - String serialized = SERIALIZER.serializeString(original); + byte[] serialized = SERIALIZER.serialize(original); recreated = SERIALIZER.deserialize(serialized, ActorReminderParams.class); } catch(Exception e) { @@ -56,7 +56,7 @@ public void withState() { Assert.fail(); } - Assert.assertEquals(original.getData(), recreated.getData()); + Assert.assertArrayEquals(original.getData(), recreated.getData()); Assert.assertEquals(original.getDueTime(), recreated.getDueTime()); Assert.assertEquals(original.getPeriod(), recreated.getPeriod()); } diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorRuntimeTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorRuntimeTest.java index 93aea13259..004fbf2a0e 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorRuntimeTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorRuntimeTest.java @@ -6,7 +6,7 @@ package io.dapr.actors.runtime; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -27,8 +27,8 @@ public interface MyActor { String say(); } - @ActorType(Name = ACTOR_NAME) - public static class MyActorImpl extends AbstractActor implements Actor, MyActor { + @ActorType(name = ACTOR_NAME) + public static class MyActorImpl extends AbstractActor implements MyActor { public MyActorImpl(ActorRuntimeContext runtimeContext, ActorId id) { super(runtimeContext, id); @@ -39,7 +39,7 @@ public String say() { } } - private static final ActorStateSerializer ACTOR_STATE_SERIALIZER = new ActorStateSerializer(); + private static final ObjectSerializer ACTOR_STATE_SERIALIZER = new ObjectSerializer(); private static Constructor constructor; @@ -67,24 +67,24 @@ public void setup() throws Exception { @Test public void registerActor() throws Exception { - this.runtime.registerActor(MyActorImpl.class); - Assert.assertTrue(this.runtime.serializeConfig().contains(ACTOR_NAME)); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); + Assert.assertTrue(new String(this.runtime.serializeConfig()).contains(ACTOR_NAME)); } @Test public void activateActor() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.activate(ACTOR_NAME, actorId).block(); } @Test public void invokeActor() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.activate(ACTOR_NAME, actorId).block(); - String response = this.runtime.invoke(ACTOR_NAME, actorId, "say", null).block(); + byte[] response = this.runtime.invoke(ACTOR_NAME, actorId, "say", null).block(); String message = ACTOR_STATE_SERIALIZER.deserialize(ACTOR_STATE_SERIALIZER.unwrapData(response), String.class); Assert.assertEquals("Nothing to say.", message); } @@ -92,7 +92,7 @@ public void invokeActor() throws Exception { @Test public void activateThendeactivateActor() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.activate(ACTOR_NAME, actorId).block(); this.runtime.deactivate(ACTOR_NAME, actorId).block(); } @@ -100,27 +100,27 @@ public void activateThendeactivateActor() throws Exception { @Test public void deactivateActor() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.deactivate(ACTOR_NAME, actorId).block(); } @Test public void lazyActivate() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.activate(ACTOR_NAME, actorId).block(); this.runtime.invoke(ACTOR_NAME, actorId, "say", null) .doOnError(e -> Assert.assertTrue(e.getMessage().contains("Could not find actor"))) .doOnSuccess(s -> Assert.fail()) - .onErrorReturn("") + .onErrorReturn("".getBytes()) .block(); } @Test public void lazyDeactivate() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); this.runtime.activate(ACTOR_NAME, actorId).block(); Mono deacticateCall = this.runtime.deactivate(ACTOR_NAME, actorId); @@ -132,16 +132,16 @@ public void lazyDeactivate() throws Exception { this.runtime.invoke(ACTOR_NAME, actorId, "say", null) .doOnError(e -> Assert.assertTrue(e.getMessage().contains("Could not find actor"))) .doOnSuccess(s -> Assert.fail()) - .onErrorReturn("") + .onErrorReturn("".getBytes()) .block(); } @Test public void lazyInvoke() throws Exception { String actorId = UUID.randomUUID().toString(); - this.runtime.registerActor(MyActorImpl.class); + this.runtime.registerActor(MyActorImpl.class, new DefaultObjectSerializer()); - Mono invokeCall = this.runtime.invoke(ACTOR_NAME, actorId, "say", null); + Mono invokeCall = this.runtime.invoke(ACTOR_NAME, actorId, "say", null); this.runtime.activate(ACTOR_NAME, actorId).block(); diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorStatefulTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorStatefulTest.java index 4b42cea016..89b33bd3bb 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorStatefulTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorStatefulTest.java @@ -8,7 +8,8 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyForTestsImpl; -import io.dapr.client.DaprClient; +import io.dapr.actors.client.DaprClientStub; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; @@ -28,6 +29,8 @@ public class ActorStatefulTest { + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); private static final Collection DEACTIVATED_ACTOR_IDS = Collections.synchronizedList(new ArrayList<>()); @@ -72,8 +75,8 @@ public interface MyActor { String getIdString(); } - @ActorType(Name = "MyActor") - public static class MyActorImpl extends AbstractActor implements MyActor, Actor, Remindable { + @ActorType(name = "MyActor") + public static class MyActorImpl extends AbstractActor implements MyActor, Remindable { private final ActorId id; @@ -477,7 +480,7 @@ public void invokeUnknownTimer() { public void invokeReminder() throws Exception { ActorProxy proxy = newActorProxy(); - String params = createReminderParams("anything"); + byte[] params = createReminderParams("anything"); this.manager.invokeReminder(proxy.getActorId(), "myreminder", params).block(); @@ -498,7 +501,7 @@ public void invokeReminderAfterDeactivate() throws Exception { this.manager.deactivateActor(proxy.getActorId()).block(); - String params = createReminderParams("anything"); + byte[] params = createReminderParams("anything"); this.manager.invokeReminder(proxy.getActorId(), "myreminder", params).block(); } @@ -597,7 +600,7 @@ private ActorProxy newActorProxy() { ActorId actorId = newActorId(); // Mock daprClient for ActorProxy only, not for runtime. - DaprClient daprClient = mock(DaprClient.class); + DaprClientStub daprClient = mock(DaprClientStub.class); when(daprClient.invokeActorMethod( eq(context.getActorTypeInformation().getName()), @@ -608,11 +611,11 @@ private ActorProxy newActorProxy() { this.manager.invokeMethod( new ActorId(invocationOnMock.getArgument(1, String.class)), invocationOnMock.getArgument(2, String.class), - Utilities.toStringOrNull(context.getActorSerializer().unwrapData( - invocationOnMock.getArgument(3, String.class)))) + INTERNAL_SERIALIZER.unwrapData( + invocationOnMock.getArgument(3, byte[].class))) .map(s -> { try { - return context.getActorSerializer().wrapData(s.getBytes()); + return INTERNAL_SERIALIZER.wrapData(s); } catch (Exception e) { throw new RuntimeException(e); } @@ -623,13 +626,14 @@ private ActorProxy newActorProxy() { return new ActorProxyForTestsImpl( context.getActorTypeInformation().getName(), actorId, - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); } - private String createReminderParams(String data) throws IOException { - ActorReminderParams params = new ActorReminderParams(data, Duration.ofSeconds(1), Duration.ofSeconds(1)); - return this.context.getActorSerializer().serializeString(params); + private byte[] createReminderParams(String data) throws IOException { + byte[] serialized = this.context.getObjectSerializer().serialize(data); + ActorReminderParams params = new ActorReminderParams(serialized, Duration.ofSeconds(1), Duration.ofSeconds(1)); + return INTERNAL_SERIALIZER.serialize(params); } private static ActorId newActorId() { @@ -650,11 +654,11 @@ private static ActorRuntimeContext createContext() { return new ActorRuntimeContext( mock(ActorRuntime.class), - new ActorStateSerializer(), + new DefaultObjectSerializer(), new DefaultActorFactory(), ActorTypeInformation.create(MyActorImpl.class), daprClient, - new DaprInMemoryStateProvider(new ActorStateSerializer()) + new DaprInMemoryStateProvider(new ObjectSerializer()) ); } } diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java index f39f446b7a..756912ebe2 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTimerTest.java @@ -28,7 +28,7 @@ public void serialize() throws IOException { null, dueTime, period); - String s = new ActorStateSerializer().serializeString(timer); + byte[] s = new ObjectSerializer().serialize(timer); String expected = "{\"period\":\"1h0m3s0ms\",\"dueTime\":\"0h7m17s0ms\", \"callback\": \"myfunction\"}"; // Deep comparison via JsonNode.equals method. @@ -53,7 +53,7 @@ public void serializeWithOneTimePeriod() throws IOException { null, dueTime, period); - String s = new ActorStateSerializer().serializeString(timer); + byte[] s = new ObjectSerializer().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\", \"callback\": \"myfunction\"}"; diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java index fe5f7780d2..e2f03328fd 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ActorTypeInformationTest.java @@ -19,7 +19,7 @@ public class ActorTypeInformationTest { /** * Actor interfaced used in this test only. */ - private interface MyActor extends Actor { + private interface MyActor { } /** @@ -82,7 +82,7 @@ public Mono receiveReminder(String reminderName, Object state, Duration du */ @Test public void renamedWithAnnotation() { - @ActorType(Name = "B") + @ActorType(name = "B") class A extends AbstractActor implements MyActor { A() { super(null, null); @@ -104,10 +104,7 @@ class A extends AbstractActor implements MyActor { */ @Test public void nonActorParentClass() { - abstract class MyAbstractClass extends AbstractActor implements MyActor { - MyAbstractClass() { - super(null, null); - } + abstract class MyAbstractClass implements MyActor { } class A extends MyAbstractClass { diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprHttpClientTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprHttpClientTest.java new file mode 100644 index 0000000000..46c2597dd2 --- /dev/null +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprHttpClientTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.actors.runtime; + +import io.dapr.client.DaprHttp; +import io.dapr.client.DaprHttpProxy; +import okhttp3.OkHttpClient; +import okhttp3.mock.Behavior; +import okhttp3.mock.MockInterceptor; +import org.junit.Before; +import org.junit.Test; +import reactor.core.publisher.Mono; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DaprHttpClientTest { + + private DaprHttpClient DaprHttpClient; + + private OkHttpClient okHttpClient; + + private MockInterceptor mockInterceptor; + + private final String EXPECTED_RESULT = "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; + + @Before + public void setUp() throws Exception { + mockInterceptor = new MockInterceptor(Behavior.UNORDERED); + okHttpClient = new OkHttpClient.Builder().addInterceptor(mockInterceptor).build(); + } + + @Test + public void getActorState() { + mockInterceptor.addRule() + .get("http://localhost:3000/v1.0/actors/DemoActor/1/state/order") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = DaprHttpClient.getActorState("DemoActor", "1", "order"); + assertEquals(new String(mono.block()), EXPECTED_RESULT); + } + + + @Test + public void saveActorStateTransactionally() { + mockInterceptor.addRule() + .put("http://localhost:3000/v1.0/actors/DemoActor/1/state") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = + DaprHttpClient.saveActorStateTransactionally("DemoActor", "1", "".getBytes()); + assertNull(mono.block()); + } + + @Test + public void registerActorReminder() { + mockInterceptor.addRule() + .put("http://localhost:3000/v1.0/actors/DemoActor/1/reminders/reminder") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = + DaprHttpClient.registerActorReminder("DemoActor", "1", "reminder", "".getBytes()); + assertNull(mono.block()); + } + + @Test + public void unregisterActorReminder() { + mockInterceptor.addRule() + .delete("http://localhost:3000/v1.0/actors/DemoActor/1/reminders/reminder") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = DaprHttpClient.unregisterActorReminder("DemoActor", "1", "reminder"); + assertNull(mono.block()); + } + + @Test + public void registerActorTimer() { + mockInterceptor.addRule() + .put("http://localhost:3000/v1.0/actors/DemoActor/1/timers/timer") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = + DaprHttpClient.registerActorTimer("DemoActor", "1", "timer", "".getBytes()); + assertNull(mono.block()); + } + + @Test + public void unregisterActorTimer() { + mockInterceptor.addRule() + .delete("http://localhost:3000/v1.0/actors/DemoActor/1/timers/timer") + .respond(EXPECTED_RESULT); + DaprHttp daprHttp = new DaprHttpProxy(3000, okHttpClient); + DaprHttpClient = new DaprHttpClient(daprHttp); + Mono mono = DaprHttpClient.unregisterActorTimer("DemoActor", "1", "timer"); + assertNull(mono.block()); + } + +} \ No newline at end of file diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprInMemoryStateProvider.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprInMemoryStateProvider.java index f0d74c6c31..621f23dc3f 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprInMemoryStateProvider.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprInMemoryStateProvider.java @@ -6,7 +6,6 @@ package io.dapr.actors.runtime; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; import reactor.core.publisher.Mono; import java.io.IOException; @@ -20,9 +19,9 @@ public class DaprInMemoryStateProvider extends DaprStateAsyncProvider { private static final Map stateStore = new HashMap<>(); - private final ActorStateSerializer serializer; + private final ObjectSerializer serializer; - DaprInMemoryStateProvider(ActorStateSerializer serializer) { + DaprInMemoryStateProvider(ObjectSerializer serializer) { super(null, null); this.serializer = serializer; } diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprStateAsyncProviderTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprStateAsyncProviderTest.java index 5320b59b77..562cd87d25 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprStateAsyncProviderTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/DaprStateAsyncProviderTest.java @@ -8,12 +8,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; +import io.dapr.client.DaprObjectSerializer; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; import java.io.IOException; +import java.util.Arrays; import java.util.Objects; import static org.mockito.ArgumentMatchers.*; @@ -24,7 +26,7 @@ */ public class DaprStateAsyncProviderTest { - private static final ActorStateSerializer SERIALIZER = new ActorStateSerializer(); + private static final DaprObjectSerializer SERIALIZER = new DefaultObjectSerializer(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @@ -106,15 +108,16 @@ public void happyCaseApply() { String key = operation.get("request").get("key").asText(); JsonNode valueNode = operation.get("request").get("value"); + byte[] value = (valueNode == null) ? null : valueNode.binaryValue(); foundInsertName |= "upsert".equals(opName) && "name".equals(key) && - "Jon Doe".equals(valueNode.asText()); + Arrays.equals(SERIALIZER.serialize("Jon Doe"), value); foundUpdateZipcode |= "upsert".equals(opName) && "zipcode".equals(key) && - "98011".equals(valueNode.asText()); + Arrays.equals(SERIALIZER.serialize(98011), value); foundDeleteFlag |= "delete".equals(opName) && "flag".equals(key) && - (valueNode == null); + (value == null); } return foundInsertName && foundUpdateZipcode && foundDeleteFlag; @@ -129,7 +132,7 @@ public void happyCaseApply() { provider.apply("MyActor", new ActorId("123"), createInsertChange("name", "Jon Doe"), - createUpdateChange("zipcode", "98011"), + createUpdateChange("zipcode", 98011), createDeleteChange("flag")) .block(); @@ -137,39 +140,39 @@ public void happyCaseApply() { } @Test - public void happyCaseLoad() { + public void happyCaseLoad() throws Exception { DaprClient daprClient = mock(DaprClient.class); when(daprClient .getActorState(any(), any(), eq("name"))) - .thenReturn(Mono.just("Jon Doe")); + .thenReturn(Mono.just(SERIALIZER.serialize("Jon Doe"))); when(daprClient .getActorState(any(), any(), eq("zipcode"))) - .thenReturn(Mono.just("98021")); + .thenReturn(Mono.just(SERIALIZER.serialize(98021))); when(daprClient .getActorState(any(), any(), eq("goals"))) - .thenReturn(Mono.just("98")); + .thenReturn(Mono.just(SERIALIZER.serialize(98))); when(daprClient .getActorState(any(), any(), eq("balance"))) - .thenReturn(Mono.just("46.55")); + .thenReturn(Mono.just(SERIALIZER.serialize(46.55))); when(daprClient .getActorState(any(), any(), eq("active"))) - .thenReturn(Mono.just("true")); + .thenReturn(Mono.just(SERIALIZER.serialize(true))); when(daprClient .getActorState(any(), any(), eq("customer"))) - .thenReturn(Mono.just("{ \"id\": 1000, \"name\": \"Roxane\"}")); + .thenReturn(Mono.just("{ \"id\": 1000, \"name\": \"Roxane\"}".getBytes())); when(daprClient .getActorState(any(), any(), eq("anotherCustomer"))) - .thenReturn(Mono.just("{ \"id\": 2000, \"name\": \"Max\"}")); + .thenReturn(Mono.just("{ \"id\": 2000, \"name\": \"Max\"}".getBytes())); when(daprClient .getActorState(any(), any(), eq("nullCustomer"))) - .thenReturn(Mono.just("")); + .thenReturn(Mono.empty()); DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, SERIALIZER); Assert.assertEquals("Jon Doe", provider.load("MyActor", new ActorId("123"), "name", String.class).block()); - Assert.assertEquals("98021", - provider.load("MyActor", new ActorId("123"), "zipcode", String.class).block()); + Assert.assertEquals(98021, + (int)provider.load("MyActor", new ActorId("123"), "zipcode", int.class).block()); Assert.assertEquals(98, (int) provider.load("MyActor", new ActorId("123"), "goals", int.class).block()); Assert.assertEquals(98, @@ -193,33 +196,33 @@ public void happyCaseContains() { // Keys that exists. when(daprClient .getActorState(any(), any(), eq("name"))) - .thenReturn(Mono.just("Jon Doe")); + .thenReturn(Mono.just("Jon Doe".getBytes())); when(daprClient .getActorState(any(), any(), eq("zipcode"))) - .thenReturn(Mono.just("98021")); + .thenReturn(Mono.just("98021".getBytes())); when(daprClient .getActorState(any(), any(), eq("goals"))) - .thenReturn(Mono.just("98")); + .thenReturn(Mono.just("98".getBytes())); when(daprClient .getActorState(any(), any(), eq("balance"))) - .thenReturn(Mono.just("46.55")); + .thenReturn(Mono.just("46.55".getBytes())); when(daprClient .getActorState(any(), any(), eq("active"))) - .thenReturn(Mono.just("true")); + .thenReturn(Mono.just("true".getBytes())); when(daprClient .getActorState(any(), any(), eq("customer"))) - .thenReturn(Mono.just("{ \"id\": \"3000\", \"name\": \"Ely\" }")); + .thenReturn(Mono.just("{ \"id\": \"3000\", \"name\": \"Ely\" }".getBytes())); // Keys that do not exist. when(daprClient .getActorState(any(), any(), eq("Does not exist"))) - .thenReturn(Mono.just("")); + .thenReturn(Mono.empty()); when(daprClient .getActorState(any(), any(), eq("NAME"))) - .thenReturn(Mono.just("")); + .thenReturn(Mono.empty()); when(daprClient .getActorState(any(), any(), eq(null))) - .thenReturn(Mono.just("")); + .thenReturn(Mono.empty()); DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, SERIALIZER); diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DefaultActorFactoryTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DefaultActorFactoryTest.java index 0a00e31e74..290cfa539b 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/DefaultActorFactoryTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/DefaultActorFactoryTest.java @@ -6,7 +6,7 @@ package io.dapr.actors.runtime; import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; +import io.dapr.client.DaprObjectSerializer; import org.junit.Assert; import org.junit.Test; @@ -20,7 +20,7 @@ public class DefaultActorFactoryTest { /** * A compliant implementation of Actor to be used in the tests below. */ - static class MyActor extends AbstractActor implements Actor { + static class MyActor extends AbstractActor { ActorRuntimeContext context; @@ -36,7 +36,7 @@ public MyActor(ActorRuntimeContext context, ActorId actorId) { /** * A non-compliant implementation of Actor to be used in the tests below. */ - static class InvalidActor extends AbstractActor implements Actor { + static class InvalidActor extends AbstractActor { InvalidActor() { super(null, null); } @@ -72,7 +72,7 @@ public void noValidConstructor() { private static ActorRuntimeContext createActorRuntimeContext(Class clazz) { return new ActorRuntimeContext( mock(ActorRuntime.class), - mock(ActorStateSerializer.class), + mock(DaprObjectSerializer.class), mock(ActorFactory.class), ActorTypeInformation.create(clazz), mock(DaprClient.class), diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DerivedActorTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DerivedActorTest.java index 12f05967f8..f040554eba 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/DerivedActorTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/DerivedActorTest.java @@ -8,18 +8,12 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyForTestsImpl; -import io.dapr.client.DaprClient; +import io.dapr.actors.client.DaprClientStub; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; -import java.io.IOException; -import java.io.NotSerializableException; -import java.nio.charset.IllegalCharsetNameException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import static org.mockito.ArgumentMatchers.any; @@ -28,9 +22,12 @@ import static org.mockito.Mockito.when; public class DerivedActorTest { + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); private final ActorRuntimeContext context = createContext(); + private ActorManager manager = new ActorManager<>(context); public interface MyActor { @@ -54,8 +51,8 @@ public interface MyActor { Mono classInClassOut(MyData input); } - @ActorType(Name = "MyActor") - public static class ActorParent extends AbstractActor implements MyActor, Actor { + @ActorType(name = "MyActor") + public static class ActorParent extends AbstractActor implements MyActor { private final ActorId id; private boolean activated; private boolean methodReturningVoidInvoked; @@ -156,7 +153,7 @@ public Mono classInClassOut(MyData input) { } } - public static class ActorChild extends ActorParent implements MyActor, Actor { + public static class ActorChild extends ActorParent implements MyActor { private final ActorId id; private boolean activated; @@ -327,7 +324,7 @@ private ActorProxy createActorProxyForActorChild() { ActorId actorId = newActorId(); // Mock daprClient for ActorProxy only, not for runtime. - DaprClient daprClient = mock(DaprClient.class); + DaprClientStub daprClient = mock(DaprClientStub.class); when(daprClient.invokeActorMethod( eq(context.getActorTypeInformation().getName()), @@ -338,11 +335,11 @@ private ActorProxy createActorProxyForActorChild() { this.manager.invokeMethod( new ActorId(invocationOnMock.getArgument(1, String.class)), invocationOnMock.getArgument(2, String.class), - Utilities.toStringOrNull(context.getActorSerializer().unwrapData( - invocationOnMock.getArgument(3, String.class)))) + INTERNAL_SERIALIZER.unwrapData( + invocationOnMock.getArgument(3, byte[].class))) .map(s -> { try { - return context.getActorSerializer().wrapData(s.getBytes()); + return INTERNAL_SERIALIZER.wrapData(s); } catch (Exception e) { throw new RuntimeException(e); } @@ -353,7 +350,7 @@ private ActorProxy createActorProxyForActorChild() { return new ActorProxyForTestsImpl( context.getActorTypeInformation().getName(), actorId, - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); } @@ -367,7 +364,7 @@ private static ActorRuntimeContext createContext() { return new ActorRuntimeContext( mock(ActorRuntime.class), - new ActorStateSerializer(), + new DefaultObjectSerializer(), new DefaultActorFactory(), ActorTypeInformation.create(ActorChild.class), daprClient, diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/ThrowFromPreAndPostActorMethodsTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/ThrowFromPreAndPostActorMethodsTest.java index d783337d07..c287307a5f 100644 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/ThrowFromPreAndPostActorMethodsTest.java +++ b/sdk-actors/src/test/java/io/dapr/actors/runtime/ThrowFromPreAndPostActorMethodsTest.java @@ -8,18 +8,12 @@ import io.dapr.actors.ActorId; import io.dapr.actors.client.ActorProxy; import io.dapr.actors.client.ActorProxyForTestsImpl; -import io.dapr.client.DaprClient; +import io.dapr.actors.client.DaprClientStub; +import io.dapr.client.DefaultObjectSerializer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Mono; -import java.io.IOException; -import java.io.NotSerializableException; -import java.nio.charset.IllegalCharsetNameException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import static org.mockito.ArgumentMatchers.any; @@ -28,17 +22,21 @@ import static org.mockito.Mockito.when; public class ThrowFromPreAndPostActorMethodsTest { + + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + private static final AtomicInteger ACTOR_ID_COUNT = new AtomicInteger(); private final ActorRuntimeContext context = createContext(); + private ActorManager manager = new ActorManager<>(context); public interface MyActor { Mono stringInBooleanOut(String input); } - @ActorType(Name = "MyActor") - public static class ActorParent extends AbstractActor implements MyActor, Actor { + @ActorType(name = "MyActor") + public static class ActorParent extends AbstractActor implements MyActor { private final ActorId id; private boolean activated; private boolean methodReturningVoidInvoked; @@ -68,7 +66,7 @@ public Mono stringInBooleanOut(String s) { } } - public static class ActorChild extends ActorParent implements MyActor, Actor { + public static class ActorChild extends ActorParent implements MyActor { private final ActorId id; private boolean activated; @@ -145,7 +143,7 @@ private ActorProxy createActorProxyForActorChild() { ActorId actorId = newActorId(); // Mock daprClient for ActorProxy only, not for runtime. - DaprClient daprClient = mock(DaprClient.class); + DaprClientStub daprClient = mock(DaprClientStub.class); when(daprClient.invokeActorMethod( eq(context.getActorTypeInformation().getName()), @@ -156,11 +154,11 @@ private ActorProxy createActorProxyForActorChild() { this.manager.invokeMethod( new ActorId(invocationOnMock.getArgument(1, String.class)), invocationOnMock.getArgument(2, String.class), - Utilities.toStringOrNull(context.getActorSerializer().unwrapData( - invocationOnMock.getArgument(3, String.class) ))) + INTERNAL_SERIALIZER.unwrapData( + invocationOnMock.getArgument(3, byte[].class))) .map(s -> { try { - return context.getActorSerializer().wrapData(s.getBytes()); + return INTERNAL_SERIALIZER.wrapData(s); } catch (Exception e) { throw new RuntimeException(e); } @@ -171,7 +169,7 @@ private ActorProxy createActorProxyForActorChild() { return new ActorProxyForTestsImpl( context.getActorTypeInformation().getName(), actorId, - new ActorStateSerializer(), + new DefaultObjectSerializer(), daprClient); } @@ -185,7 +183,7 @@ private static ActorRuntimeContext createContext() { return new ActorRuntimeContext( mock(ActorRuntime.class), - new ActorStateSerializer(), + new DefaultObjectSerializer(), new DefaultActorFactory(), ActorTypeInformation.create(ActorChild.class), daprClient, diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/Utilities.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/Utilities.java deleted file mode 100644 index 4d9fd893d6..0000000000 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/Utilities.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.actors.runtime; - -import io.dapr.actors.ActorId; -import io.dapr.client.DaprClient; -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.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Utilities - */ -class Utilities { - static String toStringOrNull(byte[] s) { - if (s == null) { - return null; - } - - return new String(s); - } -} diff --git a/sdk-actors/src/test/java/io/dapr/client/DaprHttpProxy.java b/sdk-actors/src/test/java/io/dapr/client/DaprHttpProxy.java new file mode 100644 index 0000000000..fe8bb8873f --- /dev/null +++ b/sdk-actors/src/test/java/io/dapr/client/DaprHttpProxy.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.client; + +import okhttp3.OkHttpClient; + +public class DaprHttpProxy extends io.dapr.client.DaprHttp { + + public DaprHttpProxy(int port, OkHttpClient httpClient) { + super(port, httpClient); + } + +} diff --git a/sdk/src/main/java/io/dapr/client/DaprClient.java b/sdk/src/main/java/io/dapr/client/DaprClient.java index 8c6fb07383..6727f3e3e7 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprClient.java @@ -116,111 +116,79 @@ public interface DaprClient { /** * Retrieve a State based on their key. - * @param state The key of the State to be retrieved. - * @param clazz the Type of State needed as return. - * @param the Type of the return. + * + * @param state State to be re-retrieved. + * @param clazz The Type of State needed as return. + * @param The Type of the return. * @return A Mono Plan for the requested State. */ Mono> getState(State state, Class clazz); /** - * Save/Update a list of states. - * @param states the States to be saved. - * @param the Type of the State. - * @return a Mono plan of type Void. - */ - Mono saveStates(List> states); - - /** - * Save/Update a state. - * @param key the key of the state. - * @param etag the etag to be used. - * @param value the value of the state. - * @param options the Options to use for each state. - * @param the Type of the State. - * @return a Mono plan of type Void. - */ - Mono saveState(String key, String etag, T value, StateOptions options); - - /** - * Delete a state. - * - * @param state 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(State state); - - /** - * Invokes an Actor method on Dapr. + * Retrieve a State based on their key. * - * @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. + * @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. + * @return A Mono Plan for the requested State. */ - Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload); + Mono> getState(String key, Class clazz); /** - * Gets a state from Dapr's Actor. + * Retrieve a State based on their key. * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param keyName State name. - * @return Asynchronous result with current state value. + * @param key The key of the State to be retrieved. + * @param etag Optional etag for conditional get + * @param options Optional settings for retrieve operation. + * @param clazz The Type of State needed as return. + * @param The Type of the return. + * @return A Mono Plan for the requested State. */ - Mono getActorState(String actorType, String actorId, String keyName); + Mono> getState(String key, String etag, StateOptions options, Class clazz); /** - * Saves state batch to Dapr. + * Save/Update a list of states. * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param data State to be saved. - * @return Asynchronous void result. + * @param states the States to be saved. + * @return a Mono plan of type Void. */ - Mono saveActorStateTransactionally(String actorType, String actorId, String data); + Mono saveStates(List> states); /** - * Register a reminder. + * Save/Update a state. * - * @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. + * @param key the key of the state. + * @param value the value of the state. + * @return a Mono plan of type Void. */ - Mono registerActorReminder(String actorType, String actorId, String reminderName, String data); + Mono saveState(String key, Object value); /** - * Unregisters a reminder. + * Save/Update a state. * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param reminderName Name of reminder to be unregistered. - * @return Asynchronous void result. + * @param key the key of the state. + * @param etag the etag to be used. + * @param value the value of the state. + * @param options the Options to use for each state. + * @return a Mono plan of type Void. */ - Mono unregisterActorReminder(String actorType, String actorId, String reminderName); + Mono saveState(String key, String etag, Object value, StateOptions options); /** - * Registers a timer. + * Delete a state. * - * @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. + * @param key The key of the State to be removed. + * @return a Mono plan of type Void. */ - Mono registerActorTimer(String actorType, String actorId, String timerName, String data); + Mono deleteState(String key); /** - * Unregisters a timer. + * Delete a state. * - * @param actorType Type of actor. - * @param actorId Actor Identifier. - * @param timerName Name of timer to be unregistered. - * @return Asynchronous void result. + * @param key The key of the State to be removed. + * @param etag Optional etag for conditional delete. + * @param options Optional settings for state operation. + * @return a Mono plan of type Void. */ - Mono unregisterActorTimer(String actorType, String actorId, String timerName); + Mono deleteState(String key, String etag, StateOptions options); } diff --git a/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java b/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java index 221f9e21b4..9385eb30f0 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientBuilder.java @@ -20,24 +20,38 @@ public class DaprClientBuilder { /** - * Default port for Dapr after checking environment variable. + * HTTP port for Dapr after checking environment variable. */ - private static final int port = DaprClientBuilder.getEnvPortOrDefault(); + private static final int HTTP_PORT = DaprClientBuilder.getEnvHttpPortOrDefault( + Constants.ENV_DAPR_HTTP_PORT, Constants.DEFAULT_HTTP_PORT); /** - * Unique instance of httpClient to be shared. + * GRPC port for Dapr after checking environment variable. */ - private static volatile DaprClientHttpAdapter daprHttClient; + private static final int GRPC_PORT = DaprClientBuilder.getEnvHttpPortOrDefault( + Constants.ENV_DAPR_GRPC_PORT, Constants.DEFAULT_GRPC_PORT); /** - * Tries to get a valid port from environment variable or returns default. + * Default serializer. + */ + private static final DaprObjectSerializer DEFAULT_SERIALIZER = new DefaultObjectSerializer(); + + /** + * Serializer used for objects in DaprClient. + */ + private final DaprObjectSerializer serializer; + + /** + * Finds the port defined by env variable or sticks to default. + * @param envName Name of env variable with the port. + * @param defaultPort Default port if cannot find a valid port. * - * @return Port defined in env variable or default. + * @return Port from env variable or default. */ - private static int getEnvPortOrDefault() { - String envPort = System.getenv(Constants.ENV_DAPR_HTTP_PORT); + private static int getEnvHttpPortOrDefault(String envName, int defaultPort) { + String envPort = System.getenv(envName); if (envPort == null || envPort.trim().isEmpty()) { - return Constants.DEFAULT_PORT; + return defaultPort; } try { @@ -46,7 +60,20 @@ private static int getEnvPortOrDefault() { e.printStackTrace(); } - return Constants.DEFAULT_PORT; + return defaultPort; + } + + /** + * Creates a constructor for DaprClient. + * + * @param serializer Serializer for objects to be sent and received from Dapr. + */ + public DaprClientBuilder(DaprObjectSerializer serializer) { + if (serializer == null) { + throw new IllegalArgumentException("Serializer is required"); + } + + this.serializer = serializer; } /** @@ -66,35 +93,24 @@ public DaprClient build() { * @throws java.lang.IllegalStateException if either host is missing or if port is missing or a negative number. */ private DaprClient buildDaprClientGrpc() { - if (port <= 0) { + if (GRPC_PORT <= 0) { throw new IllegalStateException("Invalid port."); } - ManagedChannel channel = ManagedChannelBuilder.forAddress(Constants.DEFAULT_HOSTNAME, port).usePlaintext().build(); - return new DaprClientGrpcAdapter(DaprGrpc.newFutureStub(channel)); + ManagedChannel channel = ManagedChannelBuilder.forAddress(Constants.DEFAULT_HOSTNAME, GRPC_PORT).usePlaintext().build(); + return new DaprClientGrpcAdapter(DaprGrpc.newFutureStub(channel), new DefaultObjectSerializer()); } /** - * Creates and instance of the HTTP CLient. - * If an okhttp3.OkHttpClient.Builder has not been provided, a defult builder will be used. + * Creates and instance of DaprClient over HTTP. * - * @return + * @return DaprClient over HTTP. */ private DaprClient buildDaprClientHttp() { - int port=DaprClientBuilder.getEnvPortOrDefault(); - if (port <= 0) { + if (HTTP_PORT <= 0) { throw new IllegalStateException("Invalid port."); } - if (this.daprHttClient == null) { - synchronized (DaprClientBuilder.class) { - if (this.daprHttClient == null) { - OkHttpClient okHttpClient = new OkHttpClient.Builder().callTimeout(Duration.ofSeconds(60)) - .build(); - DaprHttp daprHtt = new DaprHttp(port, okHttpClient); - this.daprHttClient = new DaprClientHttpAdapter(daprHtt); - } - - } - } - return this.daprHttClient; + OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + DaprHttp daprHttp = new DaprHttp(HTTP_PORT, okHttpClient); + return new DaprClientHttpAdapter(daprHttp, this.serializer); } } diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index 15f88a9bf7..3a2694f2ff 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -14,7 +14,6 @@ import io.dapr.client.domain.State; import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.Verb; -import io.dapr.utils.ObjectSerializer; import reactor.core.publisher.Mono; import java.io.IOException; @@ -28,6 +27,11 @@ */ class DaprClientGrpcAdapter implements DaprClient { + /** + * Serializer for internal objects. + */ + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + /** * The GRPC client to be used * @@ -36,9 +40,9 @@ class DaprClientGrpcAdapter implements DaprClient { private DaprGrpc.DaprFutureStub client; /** - * A utitlity class for serialize and deserialize the messages sent and retrived by the client. + * A utitlity class for serialize and deserialize the messages sent and retrieved by the client. */ - private ObjectSerializer objectSerializer; + private DaprObjectSerializer objectSerializer; /** * Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder @@ -46,9 +50,9 @@ class DaprClientGrpcAdapter implements DaprClient { * @param futureClient * @see io.dapr.client.DaprClientBuilder */ - DaprClientGrpcAdapter(DaprGrpc.DaprFutureStub futureClient) { + DaprClientGrpcAdapter(DaprGrpc.DaprFutureStub futureClient, DaprObjectSerializer serializer) { client = futureClient; - objectSerializer = new ObjectSerializer(); + objectSerializer = serializer; } /** @@ -120,7 +124,7 @@ public Mono invokeService(Verb verb, String appId, String method, R re */ @Override public Mono invokeService(Verb verb, String appId, String method, Map metadata) { - return this.invokeService(verb, appId, method, null, metadata, byte[].class).then(); + return this.invokeService(verb, appId, method, null, metadata, Void.class).then(); } /** @@ -154,17 +158,31 @@ public Mono invokeBinding(String name, T request) { } /** - * @return Returns an io.dapr.client.domain.StateKeyValue - *

* {@inheritDoc} */ @Override public Mono> getState(State state, Class clazz) { + return this.getState(state.getKey(), state.getEtag(), state.getOptions(), clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono> getState(String key, Class clazz) { + return this.getState(key, null, null, clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono> getState(String key, String etag, StateOptions options, Class clazz) { try { DaprProtos.GetStateEnvelope.Builder builder = DaprProtos.GetStateEnvelope.newBuilder() - .setKey(state.getKey()); - if (state.getOptions() != null && state.getOptions().getConsistency() != null) { - builder.setConsistency(state.getOptions().getConsistency().getValue()); + .setKey(key); + if (options != null && options.getConsistency() != null) { + builder.setConsistency(options.getConsistency().getValue()); } DaprProtos.GetStateEnvelope envelope = builder.build(); @@ -176,14 +194,20 @@ public Mono> getState(State state, Class clazz) { } catch (NullPointerException npe) { return null; } - return buildStateKeyValue(response, state.getKey(), state.getOptions(), clazz); + return buildStateKeyValue(response, key, options, clazz); }); } catch (Exception ex) { return Mono.error(ex); } } - private State buildStateKeyValue(DaprProtos.GetStateResponseEnvelope response, String requestedKey, StateOptions stateOptions, Class clazz) throws IOException { - T value = objectSerializer.deserialize(Optional.ofNullable(response.getData().getValue().toByteArray()).orElse(null), clazz); + private State buildStateKeyValue( + DaprProtos.GetStateResponseEnvelope response, + String requestedKey, + StateOptions stateOptions, + Class clazz) throws IOException { + ByteString payload = response.getData().getValue(); + byte[] data = payload == null ? null : payload.toByteArray(); + T value = objectSerializer.deserialize(data, clazz); String etag = response.getEtag(); String key = requestedKey; return new State<>(value, key, etag, stateOptions); @@ -193,11 +217,12 @@ private State buildStateKeyValue(DaprProtos.GetStateResponseEnvelope resp * {@inheritDoc} */ @Override - public Mono saveStates(List> states) { + public Mono saveStates(List> states) { try { DaprProtos.SaveStateEnvelope.Builder builder = DaprProtos.SaveStateEnvelope.newBuilder(); for (State state : states) { - builder.addRequests(buildStateRequest(state).build()); } + builder.addRequests(buildStateRequest(state).build()); + } DaprProtos.SaveStateEnvelope envelope = builder.build(); ListenableFuture futureEmpty = client.saveState(envelope); @@ -215,12 +240,16 @@ public Mono saveStates(List> states) { } private DaprProtos.StateRequest.Builder buildStateRequest(State state) throws IOException { - byte[] byteState = objectSerializer.serialize(state.getValue()); - Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteState)).build(); - DaprProtos.StateRequest.Builder stateBuilder = DaprProtos.StateRequest.newBuilder() - .setEtag(state.getEtag()) - .setKey(state.getKey()) - .setValue(data); + byte[] bytes = objectSerializer.serialize(state.getValue()); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(bytes)).build(); + DaprProtos.StateRequest.Builder stateBuilder = DaprProtos.StateRequest.newBuilder(); + if (state.getEtag() != null) { + stateBuilder.setEtag(state.getEtag()); + } + if (data != null) { + stateBuilder.setValue(data); + } + stateBuilder.setKey(state.getKey()); DaprProtos.StateRequestOptions.Builder optionBuilder = null; if (state.getOptions() != null) { StateOptions options = state.getOptions(); @@ -234,7 +263,9 @@ private DaprProtos.StateRequest.Builder buildStateRequest(State state) th .setSeconds(retryPolicy.getInterval().getSeconds()); retryPolicyBuilder.setInterval(durationBuilder.build()); } - retryPolicyBuilder.setThreshold(objectSerializer.deserialize(retryPolicy.getThreshold(), int.class)); + if (retryPolicy.getThreshold() != null) { + retryPolicyBuilder.setThreshold(retryPolicy.getThreshold()); + } if (retryPolicy.getPattern() != null) { retryPolicyBuilder.setPattern(retryPolicy.getPattern().getValue()); } @@ -257,20 +288,38 @@ private DaprProtos.StateRequest.Builder buildStateRequest(State state) th return stateBuilder; } + /** + * {@inheritDoc} + */ + @Override + public Mono saveState(String key, Object value) { + return this.saveState(key, null, value, null); + } + + /** + * {@inheritDoc} + */ @Override - public Mono saveState(String key, String etag, T value, StateOptions options) { - State state = new State<>(value, key, etag, options); - return saveStates(Arrays.asList(state)); + public Mono saveState(String key, String etag, Object value, StateOptions options) { + State state = new State<>(value, key, etag, options); + return this.saveStates(Arrays.asList(state)); } /** * {@inheritDoc} */ @Override - public Mono deleteState(State state) { + public Mono deleteState(String key) { + return this.deleteState(key, null, null); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono deleteState(String key, String etag, StateOptions options) { try { DaprProtos.StateOptions.Builder optionBuilder = null; - StateOptions options = state.getOptions(); if (options != null) { optionBuilder = DaprProtos.StateOptions.newBuilder(); DaprProtos.RetryPolicy.Builder retryPolicyBuilder = null; @@ -283,7 +332,9 @@ public Mono deleteState(State state) { .setSeconds(retryPolicy.getInterval().getSeconds()); retryPolicyBuilder.setInterval(durationBuilder.build()); } - retryPolicyBuilder.setThreshold(objectSerializer.deserialize(retryPolicy.getThreshold(), int.class)); + if (retryPolicy.getThreshold() != null) { + retryPolicyBuilder.setThreshold(retryPolicy.getThreshold()); + } if (retryPolicy.getPattern() != null) { retryPolicyBuilder.setPattern(retryPolicy.getPattern().getValue()); } @@ -301,8 +352,8 @@ public Mono deleteState(State state) { } } DaprProtos.DeleteStateEnvelope.Builder builder = DaprProtos.DeleteStateEnvelope.newBuilder() - .setEtag(state.getEtag()) - .setKey(state.getKey()); + .setEtag(etag) + .setKey(key); if (optionBuilder != null) { builder.setOptions(optionBuilder.build()); } @@ -322,76 +373,6 @@ public Mono deleteState(State state) { } } - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono getActorState(String actorType, String actorId, String keyName) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono saveActorStateTransactionally(String actorType, String actorId, String data) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono registerActorReminder(String actorType, String actorId, String reminderName, String data) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono unregisterActorReminder(String actorType, String actorId, String reminderName) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono registerActorTimer(String actorType, String actorId, String timerName, String data) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - - /** - * Operation not supported for GRPC - * - * @throws UnsupportedOperationException every time is called. - */ - @Override - public Mono unregisterActorTimer(String actorType, String actorId, String timerName) { - return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); - } - /** * Builds the object io.dapr.{@link DaprProtos.InvokeServiceEnvelope} to be send based on the parameters. * diff --git a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java index ee86c8d9ae..6894057ddd 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java @@ -8,7 +8,6 @@ import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.Verb; import io.dapr.utils.Constants; -import io.dapr.utils.ObjectSerializer; import reactor.core.publisher.Mono; import java.io.IOException; @@ -22,6 +21,11 @@ */ public class DaprClientHttpAdapter implements DaprClient { + /** + * Serializer for internal objects. + */ + private static final ObjectSerializer INTERNAL_SERIALIZER = new ObjectSerializer(); + /** * The HTTP client to be used * @@ -30,19 +34,30 @@ public class DaprClientHttpAdapter implements DaprClient { private final DaprHttp client; /** - * A utitlity class for serialize and deserialize the messages sent and retrived by the client. + * A utility class for serialize and deserialize customer's objects. */ - private final ObjectSerializer objectSerializer; + private final DaprObjectSerializer objectSerializer; /** * Default access level constructor, in order to create an instance of this class use io.dapr.client.DaprClientBuilder * * @param client Dapr's http client. + * @param serializer Dapr's object serializer. * @see io.dapr.client.DaprClientBuilder */ - DaprClientHttpAdapter(DaprHttp client) { + DaprClientHttpAdapter(DaprHttp client, DaprObjectSerializer serializer) { this.client = client; - this.objectSerializer = new ObjectSerializer(); + this.objectSerializer = serializer; + } + + /** + * Constructor useful for tests. + * + * @param client Dapr's http client. + * @see io.dapr.client.DaprClientBuilder + */ + DaprClientHttpAdapter(DaprHttp client) { + this(client, new DefaultObjectSerializer()); } /** @@ -93,7 +108,12 @@ public Mono invokeService(Verb verb, String appId, String method, R re Mono response = this.client.invokeAPI(httMethod, path, null, serializedRequestBody, metadata); return response.flatMap(r -> { try { - return Mono.just(objectSerializer.deserialize(r.getBody(), clazz)); + T object = objectSerializer.deserialize(r.getBody(), clazz); + if (object == null) { + return Mono.empty(); + } + + return Mono.just(object); } catch (Exception ex) { return Mono.error(ex); } @@ -167,25 +187,41 @@ public Mono invokeBinding(String name, T request) { */ @Override public Mono> getState(State state, Class clazz) { + return this.getState(state.getKey(), state.getEtag(), state.getOptions(), clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono> getState(String key, Class clazz) { + return this.getState(key, null, null, clazz); + } + + /** + * {@inheritDoc} + */ + @Override + public Mono> getState(String key, String etag, StateOptions options, Class clazz) { try { - if (state.getKey() == null) { + if (key == null) { throw new IllegalArgumentException("Name cannot be null or empty."); } Map headers = new HashMap<>(); - if (state.getEtag() != null && !state.getEtag().trim().isEmpty()) { - headers.put(Constants.HEADER_HTTP_ETAG_ID, state.getEtag()); + if (etag != null && !etag.trim().isEmpty()) { + headers.put(Constants.HEADER_HTTP_ETAG_ID, etag); } StringBuilder url = new StringBuilder(Constants.STATE_PATH) .append("/") - .append(state.getKey()); - Map urlParameters = Optional.ofNullable(state.getOptions()).map(options -> options.getStateOptionsAsMap() ).orElse( new HashMap<>());; + .append(key); + Map urlParameters = Optional.ofNullable(options).map(o -> o.getStateOptionsAsMap() ).orElse(new HashMap<>());; return this.client .invokeAPI(DaprHttp.HttpMethods.GET.name(), url.toString(), urlParameters, headers) .flatMap(s -> { try { - return Mono.just(buildStateKeyValue(s, state.getKey(), state.getOptions(), clazz)); - }catch (Exception ex){ + return Mono.just(buildStateKeyValue(s, key, options, clazz)); + } catch (Exception ex) { return Mono.error(ex); } }); @@ -198,7 +234,7 @@ public Mono> getState(State state, Class clazz) { * {@inheritDoc} */ @Override - public Mono saveStates(List> states) { + public Mono saveStates(List> states) { try { if (states == null || states.isEmpty()) { return Mono.empty(); @@ -210,7 +246,15 @@ public Mono saveStates(List> states) { headers.put(Constants.HEADER_HTTP_ETAG_ID, etag); } final String url = Constants.STATE_PATH; - byte[] serializedStateBody = objectSerializer.serialize(states); + List> internalStateObjects = new ArrayList<>(states.size()); + for (State state : states) { + if (state == null) { + continue; + } + byte[] data = this.objectSerializer.serialize(state.getValue()); + internalStateObjects.add(new State<>(data, state.getKey(), state.getEtag(), state.getOptions())); + } + byte[] serializedStateBody = INTERNAL_SERIALIZER.serialize(states); return this.client.invokeAPI( DaprHttp.HttpMethods.POST.name(), url, null, serializedStateBody, headers).then(); } catch (Exception ex) { @@ -222,114 +266,51 @@ public Mono saveStates(List> states) { * {@inheritDoc} */ @Override - public Mono saveState(String key, String etag, T value, StateOptions options) { - State state = new State<>(value, key, etag, options); - return saveStates(Arrays.asList(state)); + public Mono saveState(String key, Object value) { + return this.saveState(key, null, value, null); } /** * {@inheritDoc} */ @Override - public Mono deleteState(State state) { - try { - if (state == null) { - throw new IllegalArgumentException("State cannot be null."); - } - if (state.getKey() == null || state.getKey().trim().isEmpty()) { - throw new IllegalArgumentException("Name cannot be null or empty."); - } - Map headers = new HashMap<>(); - if (state.getEtag() != null && !state.getEtag().trim().isEmpty()) { - headers.put(Constants.HEADER_HTTP_ETAG_ID, state.getEtag()); - } - String url = Constants.STATE_PATH + "/" + state.getKey(); - Map urlParameters = Optional.ofNullable(state.getOptions()).map(stateOptions -> stateOptions.getStateOptionsAsMap()).orElse( new HashMap<>());; - return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, urlParameters, headers).then(); - } catch (Exception ex) { - return Mono.error(ex); - } + public Mono saveState(String key, String etag, Object value, StateOptions options) { + return Mono.fromSupplier(() -> new State(value, key, etag, options)) + .flatMap(state -> saveStates(Arrays.asList(state))); } /** * {@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); - Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, null, jsonPayload, null); - return responseMono.flatMap(f -> { - try { - return Mono.just(objectSerializer.deserialize(f.getBody(), String.class)); - } catch (Exception ex) { - return Mono.error(ex); - } - }); + public Mono deleteState(String key) { + return this.deleteState(key); } /** * {@inheritDoc} */ @Override - public Mono getActorState(String actorType, String actorId, String keyName) { - String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); - Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, null, "", null); - return responseMono.flatMap(f -> { - try { - return Mono.just(objectSerializer.deserialize(f.getBody(), String.class)); - } catch (Exception ex) { - return Mono.error(ex); + public Mono deleteState(String key, String etag, StateOptions options) { + try { + if (key == null || key.trim().isEmpty()) { + throw new IllegalArgumentException("Name cannot be null or empty."); } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono saveActorStateTransactionally(String actorType, String actorId, String data) { - String url = String.format(Constants.ACTOR_STATE_RELATIVE_URL_FORMAT, actorType, actorId); - return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url, null, data, null).then(); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono registerActorReminder(String actorType, String actorId, String reminderName, String data) { - String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); - return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url, null, data, null).then(); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono unregisterActorReminder(String actorType, String actorId, String reminderName) { - String url = String.format(Constants.ACTOR_REMINDER_RELATIVE_URL_FORMAT, actorType, actorId, reminderName); - return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, null, null).then(); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono registerActorTimer(String actorType, String actorId, String timerName, String data) { - String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); - return this.client.invokeAPI(DaprHttp.HttpMethods.PUT.name(), url,null, data, null).then(); - } - - /** - * {@inheritDoc} - */ - @Override - public Mono unregisterActorTimer(String actorType, String actorId, String timerName) { - String url = String.format(Constants.ACTOR_TIMER_RELATIVE_URL_FORMAT, actorType, actorId, timerName); - return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, null, null).then(); + Map headers = new HashMap<>(); + if (etag != null && !etag.trim().isEmpty()) { + headers.put(Constants.HEADER_HTTP_ETAG_ID, etag); + } + String url = Constants.STATE_PATH + "/" + key; + Map urlParameters = Optional.ofNullable(options).map(stateOptions -> stateOptions.getStateOptionsAsMap()).orElse( new HashMap<>());; + return this.client.invokeAPI(DaprHttp.HttpMethods.DELETE.name(), url, urlParameters, headers).then(); + } catch (Exception ex) { + return Mono.error(ex); + } } /** - * Builds a StateKeyValue object based on the Response + * Builds a State object based on the Response + * * @param response The response of the HTTP Call * @param requestedKey The Key Requested. * @param clazz The Class of the Value of the state @@ -337,12 +318,13 @@ public Mono unregisterActorTimer(String actorType, String actorId, String * @return A StateKeyValue instance * @throws IOException If there's a issue deserialzing the response. */ - private State buildStateKeyValue(DaprHttp.Response response, String requestedKey, StateOptions stateOptions, Class clazz) throws IOException { + private State buildStateKeyValue( + DaprHttp.Response response, String requestedKey, StateOptions stateOptions, Class clazz) throws IOException { T value = objectSerializer.deserialize(response.getBody(), clazz); String key = requestedKey; String etag = null; if (response.getHeaders() != null && response.getHeaders().containsKey("Etag")) { - etag = objectSerializer.deserialize(response.getHeaders().get("Etag"), String.class); + etag = response.getHeaders().get("Etag"); } return new State<>(value, key, etag, stateOptions); } diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index aa49a743cf..a458998134 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -8,26 +8,21 @@ import io.dapr.exceptions.DaprError; import io.dapr.exceptions.DaprException; import io.dapr.utils.Constants; -import io.dapr.utils.ObjectSerializer; import okhttp3.*; import reactor.core.publisher.Mono; import java.io.IOException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -class DaprHttp { +public class DaprHttp { /** * HTTP Methods supported. */ - enum HttpMethods {GET, PUT, POST, DELETE;} + public enum HttpMethods {GET, PUT, POST, DELETE;} - static class Response { + public static class Response { private byte[] body; private Map headers; private int statusCode; @@ -99,6 +94,8 @@ public int getStatusCode() { * * @param method HTTP method. * @param urlString url as String. + * @param urlParameters URL parameters + * @param headers HTTP headers. * @return Asynchronous text */ public Mono invokeAPI(String method, String urlString, Map urlParameters, Map headers) { @@ -110,8 +107,10 @@ public Mono invokeAPI(String method, String urlString, Map invokeAPI(String method, String urlString, Map urlParameters, String content, Map headers) { return this.invokeAPI(method, urlString, urlParameters, content == null ? EMPTY_BYTES : content.getBytes(StandardCharsets.UTF_8), headers); @@ -122,8 +121,10 @@ public Mono invokeAPI(String method, String urlString, Map invokeAPI(String method, String urlString, Map urlParameters, byte[] content, Map headers) { return Mono.fromCallable( @@ -179,7 +180,7 @@ public Mono invokeAPI(String method, String urlString, Map { mapHeaders.put(pair.getFirst(), pair.getSecond()); }); - return new Response(result.length > 0 ? result : null, mapHeaders, response.code()); + return new Response(result == null ? EMPTY_BYTES : result, mapHeaders, response.code()); } } catch (Exception e) { throw new RuntimeException(e); diff --git a/sdk/src/main/java/io/dapr/client/DaprHttpBuilder.java b/sdk/src/main/java/io/dapr/client/DaprHttpBuilder.java new file mode 100644 index 0000000000..a00597e61e --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/DaprHttpBuilder.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.client; + +import io.dapr.utils.Constants; +import okhttp3.OkHttpClient; + +import java.time.Duration; + +/** + * A builder for the DaprHttp. + */ +public class DaprHttpBuilder { + + /** + * Default port for Dapr after checking environment variable. + */ + private static final int PORT = DaprHttpBuilder.getEnvPortOrDefault(); + + /** + * Read timeout for http calls. + */ + private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(60); + + /** + * Read timeout used to build object. + */ + private Duration readTimeout = DEFAULT_READ_TIMEOUT; + + /** + * 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 || envPort.trim().isEmpty()) { + return Constants.DEFAULT_HTTP_PORT; + } + + try { + return Integer.parseInt(envPort.trim()); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + return Constants.DEFAULT_HTTP_PORT; + } + + /** + * Sets the read timeout duration for the instance to be built. + * + * @param duration Read timeout duration. + * @return Same builder instance. + */ + public DaprHttpBuilder withReadTimeout(Duration duration) { + this.readTimeout = duration; + return this; + } + + /** + * Build an instance of the Http client based on the provided setup. + * + * @return an instance of {@link DaprHttp} + * @throws IllegalStateException if any required field is missing + */ + public DaprHttp build() { + return buildDaprHttp(); + } + + /** + * Creates and instance of the HTTP Client. + * + * @return Instance of {@link DaprHttp} + */ + private DaprHttp buildDaprHttp() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.readTimeout(DEFAULT_READ_TIMEOUT); + OkHttpClient okHttpClient = builder.build(); + return new DaprHttp(PORT, okHttpClient); + } +} diff --git a/sdk/src/main/java/io/dapr/client/DaprObjectSerializer.java b/sdk/src/main/java/io/dapr/client/DaprObjectSerializer.java new file mode 100644 index 0000000000..14cea08334 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/DaprObjectSerializer.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.client; + +import java.io.IOException; + +/** + * Serializes and deserializes application's objects. + */ +public interface DaprObjectSerializer { + + /** + * Serializes the given object as a String to be saved. + * + * @param o Object to be serialized. + * @return Serialized object. + * @throws IOException If cannot serialize. + */ + byte[] serialize(Object o) throws IOException; + + /** + * Deserializes the given String into a object. + * + * @param data Data to be deserialized. + * @param clazz Class of object to be deserialized. + * @param Type of object to be deserialized. + * @return Deserialized object. + * @throws IOException If cannot deserialize object. + */ + T deserialize(byte[] data, Class clazz) throws IOException; +} diff --git a/sdk/src/main/java/io/dapr/client/DefaultObjectSerializer.java b/sdk/src/main/java/io/dapr/client/DefaultObjectSerializer.java new file mode 100644 index 0000000000..ef95985d51 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/DefaultObjectSerializer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.client; + +import java.io.IOException; + +/** + * Default serializer/deserializer for actor state. + * + * WARNING: for production systems, it is recommended for users to provide their own serializer instead. + */ +public class DefaultObjectSerializer implements DaprObjectSerializer { + + /** + * Shared serializer for all instances of the default state serializer. + */ + public static final ObjectSerializer SERIALIZER = new ObjectSerializer(); + + /** + * {@inheritDoc} + */ + @Override + public byte[] serialize(Object o) throws IOException { + return SERIALIZER.serialize(o); + } + + /** + * {@inheritDoc} + */ + @Override + public T deserialize(byte[] data, Class clazz) throws IOException { + return SERIALIZER.deserialize(data, clazz); + } +} diff --git a/sdk/src/main/java/io/dapr/client/ObjectSerializer.java b/sdk/src/main/java/io/dapr/client/ObjectSerializer.java new file mode 100644 index 0000000000..1b40a7f634 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/ObjectSerializer.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ +package io.dapr.client; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.dapr.client.domain.CloudEvent; + +import java.io.IOException; +import java.util.Base64; + +/** + * Serializes and deserializes an internal object. + */ +public class ObjectSerializer { + + /** + * Shared Json serializer/deserializer as per Jackson's documentation. + */ + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + + /** + * Default constructor to avoid class from being instantiated outside package but still inherited. + */ + protected ObjectSerializer() { + } + + /** + * 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 In case state cannot be serialized. + */ + public byte[] serialize(Object state) throws IOException { + if (state == null) { + return null; + } + + // Have this check here to be consistent with deserialization (see deserialize() method below). + if (state instanceof byte[]) { + return (byte[])state; + } + + // Not string, not primitive, so it is a complex type: we use JSON for that. + return OBJECT_MAPPER.writeValueAsBytes(state); + } + + /** + * Deserializes the byte array into the original object. + * + * @param content 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 content cannot be deserialized. + */ + public T deserialize(byte[] content, Class clazz) throws IOException { + if (clazz == null) { + return null; + } + + if (clazz.isPrimitive()) { + return deserializePrimitives(content, clazz); + } + + if ((content == null) || (content.length == 0)) { + return (T) null; + } + + // Deserialization of GRPC response fails without this check since it does not come as base64 encoded byte[]. + if (clazz == byte[].class) { + return (T) content; + } + + if (clazz == CloudEvent.class) { + return (T) CloudEvent.deserialize(content); + } + + return OBJECT_MAPPER.readValue(content, clazz); + } + + /** + * Parses a given String to the corresponding object defined by class. + * + * @param content Value to be parsed. + * @param clazz Class of the expected result type. + * @param Result type. + * @return Result as corresponding type. + * @throws Exception if cannot deserialize primitive time. + */ + private static T deserializePrimitives(byte[] content, Class clazz) throws IOException { + if ((content == null) || (content.length == 0)) { + 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); + if (char.class == clazz) return (T) Character.valueOf(Character.MIN_VALUE); + + return null; + } + + return OBJECT_MAPPER.readValue(content, clazz); + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/CloudEvent.java b/sdk/src/main/java/io/dapr/client/domain/CloudEvent.java new file mode 100644 index 0000000000..363948e097 --- /dev/null +++ b/sdk/src/main/java/io/dapr/client/domain/CloudEvent.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + */ + +package io.dapr.client.domain; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.Objects; + +/** + * A cloud event in Dapr. + */ +public final class CloudEvent { + + /** + * Shared Json serializer/deserializer as per Jackson's documentation. + */ + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + + /** + * Identifier of the message being processed. + */ + private final String id; + + /** + * Event's source. + */ + private final String source; + + /** + * Envelope type. + */ + private final String type; + + /** + * Version of the specification. + */ + private final String specversion; + + /** + * Type of the data's content. + */ + private final String datacontenttype; + + /** + * Cloud event specs says data can be a JSON object or string. + */ + private final String data; + + /** + * Instantiates a new input request. + * @param id Identifier of the message being processed. + * @param source Source for this event. + * @param type Type of event. + * @param specversion Version of the event spec. + * @param datacontenttype Type of the payload. + * @param data Payload. + */ + public CloudEvent( + String id, + String source, + String type, + String specversion, + String datacontenttype, + String data) { + this.id = id; + this.source = source; + this.type = type; + this.specversion = specversion; + this.datacontenttype = datacontenttype; + this.data = data; + } + + /** + * Gets the identifier of the message being processed. + * @return Identifier of the message being processed. + */ + public String getId() { + return id; + } + + /** + * Gets the source for this event. + * @return Source for this event. + */ + public String getSource() { + return source; + } + + /** + * Gets the type of event. + * @return Type of event. + */ + public String getType() { + return type; + } + + /** + * Gets the version of the event spec. + * @return Version of the event spec. + */ + public String getSpecversion() { + return specversion; + } + + /** + * Gets the type of the payload. + * @return Type of the payload. + */ + public String getDatacontenttype() { + return datacontenttype; + } + + /** + * Gets the payload + * @return Payload + */ + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CloudEvent that = (CloudEvent) o; + return Objects.equals(id, that.id) && + Objects.equals(source, that.source) && + Objects.equals(type, that.type) && + Objects.equals(specversion, that.specversion) && + Objects.equals(datacontenttype, that.datacontenttype) && + Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(id, source, type, specversion, datacontenttype, data); + } + + /** + * Deserialized a message topic from Dapr. + * @param payload Payload sent from Dapr. + * @return Message (can be null if input is null) + * @throws IOException If cannot parse. + */ + public static CloudEvent deserialize(byte[] payload) throws IOException { + if (payload == null) { + return null; + } + + JsonNode node = OBJECT_MAPPER.readTree(payload); + + if (node== null) { + return null; + } + + String id = null; + if (node.has("id") && !node.get("id").isNull()) { + id = node.get("id").asText(); + } + + String source = null; + if (node.has("source") && !node.get("source").isNull()) { + source = node.get("source").asText(); + } + + String type = null; + if (node.has("type") && !node.get("type").isNull()) { + type = node.get("type").asText(); + } + + String specversion = null; + if (node.has("specversion") && !node.get("specversion").isNull()) { + specversion = node.get("specversion").asText(); + } + + String datacontenttype = null; + if (node.has("datacontenttype") && !node.get("datacontenttype").isNull()) { + datacontenttype = node.get("datacontenttype").asText(); + } + + String data = null; + if (node.has("data") && !node.get("data").isNull()) { + JsonNode dataNode = node.get("data"); + if (dataNode.isTextual()) { + data = dataNode.textValue(); + } else { + data = node.get("data").toString(); + } + } + + return new CloudEvent(id, source, type, specversion, datacontenttype, data); + } +} diff --git a/sdk/src/main/java/io/dapr/client/domain/CloudEventEnvelope.java b/sdk/src/main/java/io/dapr/client/domain/CloudEventEnvelope.java deleted file mode 100644 index 6399fa4d27..0000000000 --- a/sdk/src/main/java/io/dapr/client/domain/CloudEventEnvelope.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ - -package io.dapr.client.domain; - -import java.util.Arrays; -import java.util.Objects; - -/** - * A cloud event in Dapr. - */ -public final class CloudEventEnvelope { - - /** - * Identifier of the message being processed. - */ - private final String id; - - /** - * Event's source. - */ - private final String source; - - /** - * Envelope type. - */ - private final String type; - - /** - * Version of the specification. - */ - private final String specversion; - - /** - * Type of the data's content. - */ - private final String datacontenttype; - - /** - * Raw input payload. - */ - private final byte[] data; - - /** - * Instantiates a new input request. - * @param id Identifier of the message being processed. - * @param source Source for this event. - * @param type Type of event. - * @param specversion Version of the event spec. - * @param datacontenttype Type of the payload. - * @param data Payload. - */ - public CloudEventEnvelope( - String id, - String source, - String type, - String specversion, - String datacontenttype, - byte[] data) { - this.id = id; - this.source = source; - this.type = type; - this.specversion = specversion; - this.datacontenttype = datacontenttype; - this.data = data; - } - - /** - * Gets the identifier of the message being processed. - * @return Identifier of the message being processed. - */ - public String getId() { - return id; - } - - /** - * Gets the source for this event. - * @return Source for this event. - */ - public String getSource() { - return source; - } - - /** - * Gets the type of event. - * @return Type of event. - */ - public String getType() { - return type; - } - - /** - * Gets the version of the event spec. - * @return Version of the event spec. - */ - public String getSpecversion() { - return specversion; - } - - /** - * Gets the type of the payload. - * @return Type of the payload. - */ - public String getDatacontenttype() { - return datacontenttype; - } - - /** - * Gets the payload - * @return Payload - */ - public byte[] getData() { - return data; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CloudEventEnvelope that = (CloudEventEnvelope) o; - return Objects.equals(id, that.id) && - Objects.equals(source, that.source) && - Objects.equals(type, that.type) && - Objects.equals(specversion, that.specversion) && - Objects.equals(datacontenttype, that.datacontenttype) && - Arrays.equals(data, that.data); - } - - @Override - public int hashCode() { - int result = Objects.hash(id, source, type, specversion, datacontenttype); - result = 31 * result + Arrays.hashCode(data); - return result; - } -} diff --git a/sdk/src/main/java/io/dapr/utils/Constants.java b/sdk/src/main/java/io/dapr/utils/Constants.java index 0c99034da3..58d31939de 100644 --- a/sdk/src/main/java/io/dapr/utils/Constants.java +++ b/sdk/src/main/java/io/dapr/utils/Constants.java @@ -25,15 +25,25 @@ public final class Constants { public static final String DEFAULT_BASE_HTTP_URL = "http://" + DEFAULT_HOSTNAME; /** - * Dapr's default port. + * Dapr's default HTTP port. */ - public static final int DEFAULT_PORT = 3500; + public static final int DEFAULT_HTTP_PORT = 3500; /** - * Environment variable used to set Dapr's port. + * Dapr's default GRPC port. + */ + public static final int DEFAULT_GRPC_PORT = 50051; + + /** + * Environment variable used to set Dapr's HTTP port. */ public static final String ENV_DAPR_HTTP_PORT = "DAPR_HTTP_PORT"; + /** + * Environment variable used to set Dapr's GRPC port. + */ + public static final String ENV_DAPR_GRPC_PORT = "DAPR_GRPC_PORT"; + /** * Header used for request id in Dapr. */ diff --git a/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java b/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java deleted file mode 100644 index 477b9209e6..0000000000 --- a/sdk/src/main/java/io/dapr/utils/ObjectSerializer.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ -package io.dapr.utils; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.dapr.client.domain.CloudEventEnvelope; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Serializes and deserializes an object. - */ -public class ObjectSerializer { - - /** - * Shared Json Factory as per Jackson's documentation. - */ - 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() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .setSerializationInclusion(JsonInclude.Include.NON_NULL); - - /** - * Constant result for empty maps. - */ - private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap<>()); - - /** - * 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 byte[] serialize(T state) throws IOException { - if (state == null) { - return null; - } - - if (state instanceof byte[]) { - return (byte[])state; - } - - if (state.getClass() == String.class) { - return ((String) state).getBytes(StandardCharsets.UTF_8); - } - - if (isPrimitiveOrEquivalent(state.getClass())) { - return state.toString().getBytes(StandardCharsets.UTF_8); - } - - // Not string, not primitive, so it is a complex type: we use JSON for that. - return OBJECT_MAPPER.writeValueAsBytes(state); - } - - /** - * Serializes a given state object into String. - * - * @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 serializeString(T state) throws IOException { - if (state == null) { - return null; - } - - if (state.getClass() == String.class) { - return (String) state; - } - - 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 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 (isPrimitiveOrEquivalent(clazz)) { - return parse(value, clazz); - } - - if (value == null) { - return (T) null; - } - - if (clazz == CloudEventEnvelope.class) { - return (T) this.deserializeCloudEventEnvelope(value); - } - - if (clazz == String.class) { - return (value instanceof byte[]) - ? (T) new String((byte[])value, StandardCharsets.UTF_8) : (T) value.toString(); - } - - if (clazz == byte[].class) { - if (value instanceof String) { - return (T) value.toString().getBytes(StandardCharsets.UTF_8); - } - - return (value instanceof byte[]) - ? (T) value : null; - } - - // Not string, not primitive, not byte[], so it is a complex type: we use JSON for that. - if (value instanceof byte[]) { - if (((byte[]) value).length==0) { - return null; - } - return OBJECT_MAPPER.readValue((byte[]) value, clazz); - } - - return OBJECT_MAPPER.readValue(value.toString(), clazz); - } - - /** - * Deserialized a message topic from Dapr. - * @param payload Payload sent from Dapr. - * @return Message (can be null if input is null) - * @throws IOException If cannot parse. - */ - private CloudEventEnvelope deserializeCloudEventEnvelope(Object payload) throws IOException { - if (payload == null) { - return null; - } - - JsonNode node = null; - if (payload instanceof byte[]) { - node = OBJECT_MAPPER.readTree((byte[])payload); - } else { - node = OBJECT_MAPPER.readTree(payload.toString()); - } - - if (node== null) { - return null; - } - - String id = null; - if (node.has("id") && !node.get("id").isNull()) { - id = node.get("id").asText(); - } - - String source = null; - if (node.has("source") && !node.get("source").isNull()) { - source = node.get("source").asText(); - } - - String type = null; - if (node.has("type") && !node.get("type").isNull()) { - type = node.get("type").asText(); - } - - String specversion = null; - if (node.has("specversion") && !node.get("specversion").isNull()) { - specversion = node.get("specversion").asText(); - } - - String datacontenttype = null; - if (node.has("datacontenttype") && !node.get("datacontenttype").isNull()) { - datacontenttype = node.get("datacontenttype").asText(); - } - - byte[] data = null; - if (node.has("data") && !node.get("data").isNull()) { - try { - data = node.get("data").binaryValue(); - } catch (IOException e) { - data = node.get("data").textValue().getBytes(StandardCharsets.UTF_8); - } - } - - return new CloudEventEnvelope(id, source, type, specversion, datacontenttype, data); - } - - /** - * 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 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; - } - - String valueString = (value instanceof byte[]) ? - new String((byte[])value, StandardCharsets.UTF_8) : value.toString(); - - if (isBooleanOrPrimitive(clazz)) return (T) Boolean.valueOf(valueString); - if (isByteOrPrimitive(clazz)) return (T) Byte.valueOf(valueString); - if (isShortOrPrimitive(clazz)) return (T) Short.valueOf(valueString); - if (isIntegerOrPrimitive(clazz)) return (T) Integer.valueOf(valueString); - if (isLongOrPrimitive(clazz)) return (T) Long.valueOf(valueString); - if (isFloatOrPrimitive(clazz)) return (T) Float.valueOf(valueString); - if (isDoubleOrPrimitive(clazz)) return (T) Double.valueOf(valueString); - - return null; - } - - /** - * Determines if this is boolean type. - * @param clazz Class to be checked. - * @return True if is boolean. - */ - private static boolean isBooleanOrPrimitive(Class clazz) { - return (Boolean.class == clazz) || (boolean.class == clazz); - } - - /** - * Determines if this is byte type. - * @param clazz Class to be checked. - * @return True if is byte. - */ - private static boolean isByteOrPrimitive(Class clazz) { - return (Byte.class == clazz) || (byte.class == clazz); - } - - /** - * Determines if this is short type. - * @param clazz Class to be checked. - * @return True if is short. - */ - private static boolean isShortOrPrimitive(Class clazz) { - return (Short.class == clazz) || (short.class == clazz); - } - - /** - * Determines if this is integer type. - * @param clazz Class to be checked. - * @return True if is integer. - */ - private static boolean isIntegerOrPrimitive(Class clazz) { - return (Integer.class == clazz) || (int.class == clazz); - } - - /** - * Determines if this is long type. - * @param clazz Class to be checked. - * @return True if is long. - */ - private static boolean isLongOrPrimitive(Class clazz) { - return (Long.class == clazz) || (long.class == clazz); - } - - /** - * Determines if this is float type. - * @param clazz Class to be checked. - * @return True if is float. - */ - private static boolean isFloatOrPrimitive(Class clazz) { - return (Float.class == clazz) || (float.class == clazz); - } - - /** - * Determines if this is double type. - * @param clazz Class to be checked. - * @return True if is double. - */ - private static boolean isDoubleOrPrimitive(Class clazz) { - return (Double.class == clazz) || (double.class == clazz); - } -} diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java index 7cf087cdb6..5217e9e1dd 100644 --- a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java @@ -10,7 +10,6 @@ import io.dapr.client.domain.State; import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.Verb; -import io.dapr.utils.ObjectSerializer; import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.junit.Before; import org.junit.Test; @@ -18,7 +17,6 @@ import reactor.core.publisher.Mono; import javax.annotation.Nullable; - import java.io.IOException; import java.time.Duration; import java.util.HashMap; @@ -33,63 +31,21 @@ public class DaprClientGrpcAdapterTest { private DaprGrpc.DaprFutureStub client; - private DaprClientGrpcAdapter adater; + private DaprClientGrpcAdapter adapter; private ObjectSerializer serializer; @Before public void setup() { client = mock(DaprGrpc.DaprFutureStub.class); - adater = new DaprClientGrpcAdapter(client); + adapter = new DaprClientGrpcAdapter(client, new DefaultObjectSerializer()); serializer = new ObjectSerializer(); } - @Test(expected = UnsupportedOperationException.class) - public void unregisterActorTimerTest() { - Mono result = adater.unregisterActorTimer("actorType", "actorId", "timerName"); - result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void registerActorTimerTest() { - Mono result = adater.registerActorTimer("actorType", "actorId", "timerName", "DATA"); - result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void unregisterActorReminderTest() { - Mono result = adater.unregisterActorReminder("actorType", "actorId", "reminderName"); - result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void registerActorReminderTest() { - Mono result = adater.registerActorReminder("actorType", "actorId", "reminderName", "DATA"); - result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void saveActorStateTransactionallyTest() { - Mono result = adater.saveActorStateTransactionally("actorType", "actorId", "DATA"); - result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void getActorStateTest() { - Mono result = adater.getActorState("actorType", "actorId", "keyName"); - String state = result.block(); - } - - @Test(expected = UnsupportedOperationException.class) - public void invokeActorMethodTest() { - Mono result = adater.invokeActorMethod("actorType", "actorId", "methodName", "jsonPlayload"); - String monoResult = result.block(); - } - @Test(expected = RuntimeException.class) public void publishEventExceptionThrownTest() { when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.publishEvent("topic", "object"); + Mono result = adapter.publishEvent("topic", "object"); result.block(); } @@ -101,7 +57,7 @@ public void publishEventCallbackExceptionThrownTest() { addCallback(settableFuture, callback, directExecutor()); when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.publishEvent("topic", "object"); + Mono result = adapter.publishEvent("topic", "object"); settableFuture.setException(ex); result.block(); } @@ -113,7 +69,7 @@ public void publishEventTest() { addCallback(settableFuture, callback, directExecutor()); when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.publishEvent("topic", "object"); + Mono result = adapter.publishEvent("topic", "object"); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -127,7 +83,7 @@ public void publishEventObjectTest() { when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); MyObject event = new MyObject(1, "Event"); - Mono result = adater.publishEvent("topic", event); + Mono result = adapter.publishEvent("topic", event); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -137,7 +93,7 @@ public void publishEventObjectTest() { public void invokeBindingExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeBinding("BindingName", "request"); + Mono result = adapter.invokeBinding("BindingName", "request"); result.block(); } @@ -151,7 +107,7 @@ public void invokeBindingCallbackExceptionThrownTest() { settableFuture.setException(ex); when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeBinding("BindingName", "request"); + Mono result = adapter.invokeBinding("BindingName", "request"); result.block(); } @@ -162,7 +118,7 @@ public void invokeBindingTest() { addCallback(settableFuture, callback, directExecutor()); when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeBinding("BindingName", "request"); + Mono result = adapter.invokeBinding("BindingName", "request"); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -176,7 +132,7 @@ public void invokeBindingObjectTest() { when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) .thenReturn(settableFuture); MyObject event = new MyObject(1, "Event"); - Mono result = adater.invokeBinding("BindingName", event); + Mono result = adapter.invokeBinding("BindingName", event); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -186,7 +142,7 @@ public void invokeBindingObjectTest() { public void invokeServiceVoidExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null); result.block(); } @@ -200,7 +156,7 @@ public void invokeServiceVoidCallbackExceptionThrownTest() { settableFuture.setException(ex); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null); result.block(); } @@ -214,7 +170,7 @@ public void invokeServiceVoidTest() throws Exception { addCallback(settableFuture, callback, directExecutor()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null); settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); result.block(); assertTrue(callback.wasCalled); @@ -231,7 +187,7 @@ public void invokeServiceVoidObjectTest() throws Exception { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); MyObject request = new MyObject(1, "Event"); - Mono result = adater.invokeService(Verb.GET, "appId", "method", request, null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", request, null); settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); result.block(); assertTrue(callback.wasCalled); @@ -241,7 +197,7 @@ public void invokeServiceVoidObjectTest() throws Exception { public void invokeServiceExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null, String.class); result.block(); } @@ -254,7 +210,7 @@ public void invokeServiceCallbackExceptionThrownTest() { addCallback(settableFuture, callback, directExecutor()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null, String.class); settableFuture.setException(ex); result.block(); } @@ -270,32 +226,33 @@ public void invokeServiceTest() throws Exception { settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null, String.class); String strOutput = result.block(); assertEquals(expected, strOutput); } @Test public void invokeServiceObjectTest() throws Exception { - MyObject resultObj = new MyObject(1, "Value"); + MyObject object = new MyObject(1, "Value"); SettableFuture settableFuture = SettableFuture.create(); MockCallback callback = new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() - .setData(getAny(resultObj)).build()); + .setData(getAny(object)).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(object)).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); - String strOutput = result.block(); - assertEquals(serializer.serializeString(resultObj), strOutput); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", "request", null, MyObject.class); + MyObject resultObject = result.block(); + assertEquals(object.id, resultObject.id); + assertEquals(object.value, resultObject.value); } @Test(expected = RuntimeException.class) public void invokeServiceNoRequestBodyExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null, String.class); result.block(); } @@ -308,7 +265,7 @@ public void invokeServiceNoRequestCallbackExceptionThrownTest() { addCallback(settableFuture, callback, directExecutor()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null, String.class); settableFuture.setException(ex); result.block(); } @@ -325,26 +282,27 @@ public void invokeServiceNoRequestBodyTest() throws Exception { settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null, String.class); String strOutput = result.block(); assertEquals(expected, strOutput); } @Test public void invokeServiceNoRequestBodyObjectTest() throws Exception { - MyObject resultObj = new MyObject(1, "Value"); + MyObject object = new MyObject(1, "Value"); SettableFuture settableFuture = SettableFuture.create(); MockCallback callback = new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() - .setData(getAny(resultObj)).build()); + .setData(getAny(object)).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(object)).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); - String strOutput = result.block(); - assertEquals(serializer.serializeString(resultObj), strOutput); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null, MyObject.class); + MyObject resultObject = result.block(); + assertEquals(object.id, resultObject.id); + assertEquals(object.value, resultObject.value); } @Test(expected = RuntimeException.class) @@ -353,7 +311,7 @@ public void invokeServiceByteRequestExceptionThrownTest() throws IOException { .thenThrow(RuntimeException.class); String request = "Request"; byte[] byteRequest = serializer.serialize(request); - Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", byteRequest, null); result.block(); } @@ -368,7 +326,7 @@ public void invokeServiceByteRequestCallbackExceptionThrownTest() throws IOExcep .thenReturn(settableFuture); String request = "Request"; byte[] byteRequest = serializer.serialize(request); - Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", byteRequest, null); settableFuture.setException(ex); result.block(); } @@ -386,7 +344,7 @@ public void invokeByteRequestServiceTest() throws Exception { .thenReturn(settableFuture); String request = "Request"; byte[] byteRequest = serializer.serialize(request); - Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", byteRequest, null); byte[] byteOutput = result.block(); String strOutput = serializer.deserialize(byteOutput, String.class); assertEquals(expected, strOutput); @@ -405,7 +363,7 @@ public void invokeServiceByteRequestObjectTest() throws Exception { .thenReturn(settableFuture); String request = "Request"; byte[] byteRequest = serializer.serialize(request); - Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", byteRequest, null); byte[] byteOutput = result.block(); assertEquals(resultObj, serializer.deserialize(byteOutput, MyObject.class)); } @@ -414,7 +372,7 @@ public void invokeServiceByteRequestObjectTest() throws Exception { public void invokeServiceNoRequestNoClassBodyExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null); result.block(); } @@ -427,7 +385,7 @@ public void invokeServiceNoRequestNoClassCallbackExceptionThrownTest() { addCallback(settableFuture, callback, directExecutor()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null); settableFuture.setException(ex); result.block(); } @@ -442,7 +400,7 @@ public void invokeServiceNoRequestNoClassBodyTest() throws Exception { addCallback(settableFuture, callback, directExecutor()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null); settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); result.block(); assertTrue(callback.wasCalled); @@ -460,7 +418,7 @@ public void invokeServiceNoRequestNoClassBodyObjectTest() throws Exception { settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + Mono result = adapter.invokeService(Verb.GET, "appId", "method", null); result.block(); assertTrue(callback.wasCalled); } @@ -469,7 +427,7 @@ public void invokeServiceNoRequestNoClassBodyObjectTest() throws Exception { public void getStateExceptionThrownTest() { when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))).thenThrow(RuntimeException.class); State key = buildStateKey(null, "Key1", "ETag1", null); - Mono> result = adater.getState(key, String.class); + Mono> result = adapter.getState(key, String.class); result.block(); } @@ -483,7 +441,7 @@ public void getStateCallbackExceptionThrownTest() { when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) .thenReturn(settableFuture); State key = buildStateKey(null, "Key1", "ETag1", null); - Mono> result = adater.getState(key, String.class); + Mono> result = adapter.getState(key, String.class); settableFuture.setException(ex); result.block(); } @@ -501,7 +459,7 @@ public void getStateStringValueNoOptionsTest() throws IOException { when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) .thenReturn(settableFuture); State keyRequest = buildStateKey(null, key, etag, null); - Mono> result = adater.getState(keyRequest, String.class); + Mono> result = adapter.getState(keyRequest, String.class); settableFuture.set(responseEnvelope); assertEquals(expectedState, result.block()); } @@ -524,7 +482,7 @@ public void getStateObjectValueWithOptionsTest() throws IOException { addCallback(settableFuture, callback, directExecutor()); when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) .thenReturn(settableFuture); - Mono> result = adater.getState(keyRequest, MyObject.class); + Mono> result = adapter.getState(keyRequest, MyObject.class); settableFuture.set(responseEnvelope); assertEquals(expectedState, result.block()); } @@ -547,7 +505,7 @@ public void getStateObjectValueWithOptionsNoConcurrencyTest() throws IOException addCallback(settableFuture, callback, directExecutor()); when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) .thenReturn(settableFuture); - Mono> result = adater.getState(keyRequest, MyObject.class); + Mono> result = adapter.getState(keyRequest, MyObject.class); settableFuture.set(responseEnvelope); assertEquals(expectedState, result.block()); } @@ -556,7 +514,7 @@ public void getStateObjectValueWithOptionsNoConcurrencyTest() throws IOException public void deleteStateExceptionThrowTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))).thenThrow(RuntimeException.class); State key = buildStateKey(null, "Key1", "ETag1", null); - Mono result = adater.deleteState(key); + Mono result = adapter.deleteState(key.getKey(), key.getEtag(), key.getOptions()); result.block(); } @@ -570,7 +528,7 @@ public void deleteStateCallbackExcpetionThrownTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State key = buildStateKey(null, "Key1", "ETag1", null); - Mono result = adater.deleteState(key); + Mono result = adapter.deleteState(key.getKey(), key.getEtag(), key.getOptions()); settableFuture.setException(ex); result.block(); } @@ -585,7 +543,7 @@ public void deleteStateNoOptionsTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, null); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -603,7 +561,7 @@ public void deleteStateTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -621,7 +579,7 @@ public void deleteStateNoConsistencyTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -639,7 +597,7 @@ public void deleteStateNoConcurrencyTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -657,7 +615,7 @@ public void deleteStateNoRetryPolicyTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -675,7 +633,7 @@ public void deleteStateRetryPolicyNoDurationTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -693,7 +651,7 @@ public void deleteStateRetryPolicyNoThresholdTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -711,7 +669,7 @@ public void deleteStateRetryPolicyNoPatternTest() { when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFuture); State stateKey = buildStateKey(null, key, etag, options); - Mono result = adater.deleteState(stateKey); + Mono result = adapter.deleteState(stateKey.getKey(), stateKey.getEtag(), stateKey.getOptions()); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -723,7 +681,7 @@ public void saveStateExceptionThrownTest() { String etag = "ETag1"; String value = "State value"; when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenThrow(RuntimeException.class); - Mono result = adater.saveState(key, etag, value, null); + Mono result = adapter.saveState(key, etag, value, null); result.block(); } @@ -737,7 +695,7 @@ public void saveStateCallbackExceptionThrownTest() { MockCallback callback = new MockCallback<>(ex); addCallback(settableFuture, callback, directExecutor()); when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); - Mono result = adater.saveState(key, etag, value, null); + Mono result = adapter.saveState(key, etag, value, null); settableFuture.setException(ex); result.block(); } @@ -751,7 +709,7 @@ public void saveStateNoOptionsTest() { MockCallback callback = new MockCallback<>(Empty.newBuilder().build()); addCallback(settableFuture, callback, directExecutor()); when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); - Mono result = adater.saveState(key, etag, value, null); + Mono result = adapter.saveState(key, etag, value, null); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -768,7 +726,7 @@ public void saveStateTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, Duration.ofDays(100), 1, StateOptions.RetryPolicy.Pattern.LINEAR); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -785,7 +743,7 @@ public void saveStateNoConsistencyTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(null, StateOptions.Concurrency.FIRST_WRITE, Duration.ofDays(100), 1, StateOptions.RetryPolicy.Pattern.LINEAR); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -802,7 +760,7 @@ public void saveStateNoConcurrencyTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, null, Duration.ofDays(100), 1, StateOptions.RetryPolicy.Pattern.LINEAR); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -819,7 +777,7 @@ public void saveStateNoRetryPolicyTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, null, null, null); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -836,7 +794,7 @@ public void saveStateRetryPolicyNoDurationTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, null, 1, StateOptions.RetryPolicy.Pattern.LINEAR); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -853,7 +811,7 @@ public void saveStateRetryPolicyNoThresholdTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, Duration.ofDays(100), null, StateOptions.RetryPolicy.Pattern.LINEAR); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -870,7 +828,7 @@ public void saveStateRetryPolicyNoPatternTest() { when(client.saveState(any(io.dapr.DaprProtos.SaveStateEnvelope.class))).thenReturn(settableFuture); StateOptions options = buildStateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, Duration.ofDays(100), 1, null); - Mono result = adater.saveState(key, etag, value, options); + Mono result = adapter.saveState(key, etag, value, options); settableFuture.set(Empty.newBuilder().build()); result.block(); assertTrue(callback.wasCalled); @@ -912,17 +870,17 @@ public void getStateDeleteStateThenBlockDeleteThenBlockGet() throws Exception { futuresMap.put(key2, buildFutureGetStateEnvelop(expectedValue2, etag)); when(client.getState(argThat(new GetStateEnvelopeKeyMatcher(key1)))).thenReturn(futuresMap.get(key1)); State keyRequest1 = buildStateKey(null, key1, etag, null); - Mono> resultGet1 = adater.getState(keyRequest1, String.class); + Mono> resultGet1 = adapter.getState(keyRequest1, String.class); assertEquals(expectedState1, resultGet1.block()); State keyRequest2 = buildStateKey(null, key2, etag, null); - Mono> resultGet2 = adater.getState(keyRequest2, String.class); + Mono> resultGet2 = adapter.getState(keyRequest2, String.class); SettableFuture settableFutureDelete = SettableFuture.create(); MockCallback callbackDelete = new MockCallback<>(Empty.newBuilder().build()); addCallback(settableFutureDelete, callbackDelete, directExecutor()); when(client.deleteState(any(io.dapr.DaprProtos.DeleteStateEnvelope.class))) .thenReturn(settableFutureDelete); - Mono resultDelete = adater.deleteState(keyRequest2); + Mono resultDelete = adapter.deleteState(keyRequest2.getKey(), keyRequest2.getEtag(), keyRequest2.getOptions()); settableFutureDelete.set(Empty.newBuilder().build()); resultDelete.block(); assertTrue(callbackDelete.wasCalled); diff --git a/sdk/src/test/java/io/dapr/client/DaprClientHttpAdapterTest.java b/sdk/src/test/java/io/dapr/client/DaprClientHttpAdapterTest.java index 314a2ac929..54370a15d1 100644 --- a/sdk/src/test/java/io/dapr/client/DaprClientHttpAdapterTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprClientHttpAdapterTest.java @@ -7,7 +7,6 @@ import io.dapr.client.domain.State; import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.Verb; -import io.dapr.utils.ObjectSerializer; import okhttp3.OkHttpClient; import okhttp3.mock.Behavior; import okhttp3.mock.MockInterceptor; @@ -15,7 +14,6 @@ import org.junit.Test; import reactor.core.publisher.Mono; -import java.io.IOException; import java.util.*; import static org.junit.Assert.*; @@ -32,11 +30,8 @@ public class DaprClientHttpAdapterTest { private MockInterceptor mockInterceptor; - private ObjectSerializer serializer = new ObjectSerializer(); - private final String EXPECTED_RESULT = "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; - @Before public void setUp() throws Exception { mockInterceptor = new MockInterceptor(Behavior.UNORDERED); @@ -133,11 +128,11 @@ public void invokeServiceMethodNull() { public void invokeService() { mockInterceptor.addRule() .get("http://localhost:3000/v1.0/invoke/41/method/neworder") - .respond(EXPECTED_RESULT); + .respond("\"hello world\""); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); Mono mono = daprClientHttpAdapter.invokeService(Verb.GET, "41", "neworder", null, null, String.class); - assertEquals(mono.block(), EXPECTED_RESULT); + assertEquals("hello world", mono.block()); } @Test @@ -148,8 +143,8 @@ public void simpleInvokeService() { .respond(EXPECTED_RESULT); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.invokeService(Verb.GET, "41", "neworder", null, String.class); - assertEquals(mono.block(), EXPECTED_RESULT); + Mono mono = daprClientHttpAdapter.invokeService(Verb.GET, "41", "neworder", null, byte[].class); + assertEquals(new String(mono.block()), EXPECTED_RESULT); } @Test @@ -221,7 +216,7 @@ public void getStates() { State stateKeyNull = new State("value", null, "etag", stateOptions); mockInterceptor.addRule() .get("http://localhost:3000/v1.0/state/key") - .respond(EXPECTED_RESULT); + .respond("\"" + EXPECTED_RESULT + "\""); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); assertThrows(IllegalArgumentException.class, () -> { @@ -236,7 +231,7 @@ public void getStatesEmptyEtag() { State stateEmptyEtag = new State("value", "key", "", null); mockInterceptor.addRule() .get("http://localhost:3000/v1.0/state/key") - .respond(EXPECTED_RESULT); + .respond("\"" + EXPECTED_RESULT + "\""); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); Mono> monoEmptyEtag = daprClientHttpAdapter.getState(stateEmptyEtag, String.class); @@ -248,7 +243,7 @@ public void getStatesNullEtag() { State stateNullEtag = new State("value", "key", null, null); mockInterceptor.addRule() .get("http://localhost:3000/v1.0/state/key") - .respond(EXPECTED_RESULT); + .respond("\"" + EXPECTED_RESULT + "\""); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); Mono> monoNullEtag = daprClientHttpAdapter.getState(stateNullEtag, String.class); @@ -258,7 +253,7 @@ public void getStatesNullEtag() { @Test public void saveStates() { State stateKeyValue = new State("value", "key", "etag", null); - List> stateKeyValueList = Arrays.asList(stateKeyValue); + List> stateKeyValueList = Arrays.asList(stateKeyValue); mockInterceptor.addRule() .post("http://localhost:3000/v1.0/state") .respond(EXPECTED_RESULT); @@ -271,7 +266,7 @@ public void saveStates() { @Test public void saveStatesNull() { State stateKeyValue = new State("value", "key", "", null); - List> stateKeyValueList = new ArrayList(); + List> stateKeyValueList = new ArrayList(); mockInterceptor.addRule() .post("http://localhost:3000/v1.0/state") .respond(EXPECTED_RESULT); @@ -286,7 +281,7 @@ public void saveStatesNull() { @Test public void saveStatesEtagNull() { State stateKeyValue = new State("value", "key", null, null); - List> stateKeyValueList = Arrays.asList(stateKeyValue); + List> stateKeyValueList = Arrays.asList(stateKeyValue); mockInterceptor.addRule() .post("http://localhost:3000/v1.0/state") .respond(EXPECTED_RESULT); @@ -299,7 +294,7 @@ public void saveStatesEtagNull() { @Test public void saveStatesEtagEmpty() { State stateKeyValue = new State("value", "key", "", null); - List> stateKeyValueList = Arrays.asList(stateKeyValue); + List> stateKeyValueList = Arrays.asList(stateKeyValue); mockInterceptor.addRule() .post("http://localhost:3000/v1.0/state") .respond(EXPECTED_RESULT); @@ -331,7 +326,7 @@ public void deleteState() { .respond(EXPECTED_RESULT); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue); + Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue.getKey(), stateKeyValue.getEtag(), stateOptions); assertNull(mono.block()); } @@ -343,7 +338,7 @@ public void deleteStateNullEtag() { .respond(EXPECTED_RESULT); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue); + Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue.getKey(), stateKeyValue.getEtag(), null); assertNull(mono.block()); } @@ -355,7 +350,7 @@ public void deleteStateEmptyEtag() { .respond(EXPECTED_RESULT); daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue); + Mono mono = daprClientHttpAdapter.deleteState(stateKeyValue.getKey(), stateKeyValue.getEtag(), null); assertNull(mono.block()); } @@ -369,94 +364,13 @@ public void deleteStateIllegalArgumentException() { daprHttp = new DaprHttp(3000, okHttpClient); daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); assertThrows(IllegalArgumentException.class, () -> { - daprClientHttpAdapter.deleteState(null).block(); + daprClientHttpAdapter.deleteState(null, null, null).block(); }); assertThrows(IllegalArgumentException.class, () -> { - daprClientHttpAdapter.deleteState(stateKeyValueNull).block(); + daprClientHttpAdapter.deleteState("", null, null).block(); }); assertThrows(IllegalArgumentException.class, () -> { - daprClientHttpAdapter.deleteState(stateKeyValueEmpty).block(); + daprClientHttpAdapter.deleteState(" ", null, null).block(); }); } - - @Test - public void invokeActorMethod() throws IOException { - DaprHttp daprHttpMock = mock(DaprHttp.class); - mockInterceptor.addRule() - .post("http://localhost:3000/v1.0/actors/DemoActor/1/method/Payment") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.invokeActorMethod("DemoActor", "1", "Payment", ""); - assertEquals(mono.block(), EXPECTED_RESULT); - } - - - @Test - public void getActorState() { - mockInterceptor.addRule() - .get("http://localhost:3000/v1.0/actors/DemoActor/1/state/order") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.getActorState("DemoActor", "1", "order"); - assertEquals(mono.block(), EXPECTED_RESULT); - } - - - @Test - public void saveActorStateTransactionally() { - mockInterceptor.addRule() - .put("http://localhost:3000/v1.0/actors/DemoActor/1/state") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.saveActorStateTransactionally("DemoActor", "1", ""); - assertNull(mono.block()); - } - - @Test - public void registerActorReminder() { - mockInterceptor.addRule() - .put("http://localhost:3000/v1.0/actors/DemoActor/1/reminders/reminder") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.registerActorReminder("DemoActor", "1", "reminder", ""); - assertNull(mono.block()); - } - - @Test - public void unregisterActorReminder() { - mockInterceptor.addRule() - .delete("http://localhost:3000/v1.0/actors/DemoActor/1/reminders/reminder") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.unregisterActorReminder("DemoActor", "1", "reminder"); - assertNull(mono.block()); - } - - @Test - public void registerActorTimer() { - mockInterceptor.addRule() - .put("http://localhost:3000/v1.0/actors/DemoActor/1/timers/timer") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.registerActorTimer("DemoActor", "1", "timer", ""); - assertNull(mono.block()); - } - - @Test - public void unregisterActorTimer() { - mockInterceptor.addRule() - .delete("http://localhost:3000/v1.0/actors/DemoActor/1/timers/timer") - .respond(EXPECTED_RESULT); - DaprHttp daprHttp = new DaprHttp(3000, okHttpClient); - daprClientHttpAdapter = new DaprClientHttpAdapter(daprHttp); - Mono mono = daprClientHttpAdapter.unregisterActorTimer("DemoActor", "1", "timer"); - assertNull(mono.block()); - } - } \ No newline at end of file diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java index 25167bb5c7..85faf54539 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java @@ -6,7 +6,6 @@ import io.dapr.exceptions.DaprException; import io.dapr.utils.Constants; -import io.dapr.utils.ObjectSerializer; import okhttp3.*; import okhttp3.mock.Behavior; import okhttp3.mock.MockInterceptor; @@ -44,7 +43,7 @@ public void invokeMethod() throws IOException { headers.put("header1", "value1"); mockInterceptor.addRule() .post("http://localhost:3500/v1.0/state") - .respond(EXPECTED_RESULT); + .respond(serializer.serialize(EXPECTED_RESULT)); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono mono = daprHttp.invokeAPI("POST", "v1.0/state", null, (byte[]) null, headers); DaprHttp.Response response = mono.block(); @@ -56,7 +55,7 @@ public void invokeMethod() throws IOException { public void invokePostMethod() throws IOException { mockInterceptor.addRule() .post("http://localhost:3500/v1.0/state") - .respond(EXPECTED_RESULT) + .respond(serializer.serialize(EXPECTED_RESULT)) .addHeader("Header", "Value"); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono mono = daprHttp.invokeAPI("POST", "v1.0/state", null, "", null); @@ -69,7 +68,7 @@ public void invokePostMethod() throws IOException { public void invokeDeleteMethod() throws IOException { mockInterceptor.addRule() .delete("http://localhost:3500/v1.0/state") - .respond(EXPECTED_RESULT); + .respond(serializer.serialize(EXPECTED_RESULT)); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono mono = daprHttp.invokeAPI("DELETE", "v1.0/state", null, (String) null, null); DaprHttp.Response response = mono.block(); @@ -81,7 +80,7 @@ public void invokeDeleteMethod() throws IOException { public void invokeGetMethod() throws IOException { mockInterceptor.addRule() .get("http://localhost:3500/v1.0/get") - .respond(EXPECTED_RESULT); + .respond(serializer.serialize(EXPECTED_RESULT)); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono mono = daprHttp.invokeAPI("GET", "v1.0/get", null, null); DaprHttp.Response response = mono.block(); @@ -98,7 +97,7 @@ public void invokeMethodWithHeaders() throws IOException { urlParameters.put("orderId", "41"); mockInterceptor.addRule() .get("http://localhost:3500/v1.0/state/order?orderId=41") - .respond(EXPECTED_RESULT); + .respond(serializer.serialize(EXPECTED_RESULT)); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono mono = daprHttp.invokeAPI("GET", "v1.0/state/order", urlParameters, headers); DaprHttp.Response response = mono.block(); @@ -179,7 +178,7 @@ public void testCallbackCalledAtTheExpectedTimeTest() throws IOException { mockInterceptor.addRule() .get("http://localhost:3500/" + urlExistingState) .respond(200, ResponseBody.create(MediaType.parse("application/json"), - existingState)); + serializer.serialize(existingState))); DaprHttp daprHttp = new DaprHttp(3500, okHttpClient); Mono response = daprHttp.invokeAPI("GET", urlExistingState, null, null); assertEquals(existingState, serializer.deserialize(response.block().getBody(), String.class)); diff --git a/sdk/src/test/java/io/dapr/it/actor/DaprHttpAsyncClientIT.java b/sdk/src/test/java/io/dapr/it/actor/DaprHttpAsyncClientIT.java deleted file mode 100644 index d2ac154d0c..0000000000 --- a/sdk/src/test/java/io/dapr/it/actor/DaprHttpAsyncClientIT.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - */ -package io.dapr.it.actor; - -import io.dapr.client.DaprClient; -import io.dapr.client.DaprClientBuilder; -import io.dapr.exceptions.DaprException; -import io.dapr.it.BaseIT; -import org.junit.Assert; -import org.junit.Test; - -/** - * Integration test for the HTTP Async Client. - *

- * Requires Dapr running. - */ -public class DaprHttpAsyncClientIT extends BaseIT { - - /** - * 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() { - DaprClient daprClient = new DaprClientBuilder().build(); - daprClient - .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(); - } -} diff --git a/sdk/src/test/java/io/dapr/it/state/HttpStateClientIT.java b/sdk/src/test/java/io/dapr/it/state/HttpStateClientIT.java index ccf23f200d..197e053bde 100644 --- a/sdk/src/test/java/io/dapr/it/state/HttpStateClientIT.java +++ b/sdk/src/test/java/io/dapr/it/state/HttpStateClientIT.java @@ -7,6 +7,7 @@ import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DefaultObjectSerializer; import io.dapr.client.domain.State; import io.dapr.client.domain.StateOptions; import io.dapr.it.BaseIT; @@ -43,7 +44,7 @@ public void saveAndGetState() { final String stateKey = "myKey"; //create the http client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //creation of a dummy data MyData data = new MyData(); @@ -76,7 +77,7 @@ public void saveUpdateAndGetState() { final String stateKey = "keyToBeUpdated"; //create http DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -111,7 +112,7 @@ public void saveAndDeleteState() { final String stateKey = "myeKeyToBeDeleted"; //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); @@ -132,7 +133,7 @@ public void saveAndDeleteState() { Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); //create deferred action to delete the state - Mono deleteResponse = daprClient.deleteState(new State(stateKey, null, null)); + Mono deleteResponse = daprClient.deleteState(stateKey, null, null); //execute the delete action deleteResponse.block(); @@ -151,7 +152,7 @@ public void saveUpdateAndGetStateWithEtag() { //The key use to store the state and be updated using etags final String stateKey = "keyToBeUpdatedWithEtag"; //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -204,7 +205,7 @@ public void saveUpdateAndGetStateWithWrongEtag() { final String stateKey = "keyToBeUpdatedWithWrongEtag"; //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -255,7 +256,7 @@ public void saveUpdateAndGetStateWithWrongEtag() { public void saveAndDeleteStateWithEtag() { final String stateKey = "myeKeyToBeDeletedWithEtag"; //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -277,7 +278,7 @@ public void saveAndDeleteStateWithEtag() { Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); //Create deferred action to delete an state sending the etag - Mono deleteResponse = daprClient.deleteState(new State(stateKey, myDataResponse.getEtag(), null)); + Mono deleteResponse = daprClient.deleteState(stateKey, myDataResponse.getEtag(), null); //execute the delete of the state deleteResponse.block(); @@ -295,7 +296,7 @@ public void saveAndDeleteStateWithWrongEtag() { final String stateKey = "myeKeyToBeDeletedWithWrongEtag"; //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -317,7 +318,7 @@ public void saveAndDeleteStateWithWrongEtag() { Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); //Create deferred action to delete an state sending the incorrect etag - Mono deleteResponse = daprClient.deleteState(new State(stateKey, "99999999999", null)); + Mono deleteResponse = daprClient.deleteState(stateKey, "99999999999", null); //execute the delete of the state, this should trhow an exception deleteResponse.block(); @@ -337,7 +338,7 @@ public void saveUpdateAndGetStateWithEtagAndStateOptionsFirstWrite() { StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.FIRST_WRITE, null); //create dapr client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //create Dummy data MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -395,7 +396,7 @@ public void saveUpdateAndGetStateWithEtagAndStateOptionsLastWrite() { StateOptions stateOptions = new StateOptions(StateOptions.Consistency.STRONG, StateOptions.Concurrency.LAST_WRITE, null); //create dapr client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //create Dummy data MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -452,7 +453,7 @@ public void saveDeleteWithRetry() { StateOptions stateOptions = new StateOptions(null, null, retryPolicy); //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); @@ -476,7 +477,7 @@ public void saveDeleteWithRetry() { Assert.assertEquals("data in property B", myDataResponse.getValue().getPropertyB()); - Mono deleteResponse = daprClient.deleteState(new State(stateKey, "99999999", stateOptions)); + Mono deleteResponse = daprClient.deleteState(stateKey, "99999999", stateOptions); long start = System.currentTimeMillis(); try { @@ -500,7 +501,7 @@ public void saveUpdateWithRetry() { StateOptions stateOptions = new StateOptions(null, null, retryPolicy); //create DAPR client - DaprClient daprClient = new DaprClientBuilder().build(); + DaprClient daprClient = new DaprClientBuilder(new DefaultObjectSerializer()).build(); //Create dummy data to be store MyData data = new MyData(); data.setPropertyA("data in property A"); diff --git a/sdk/src/test/java/io/dapr/runtime/Dapr.java b/sdk/src/test/java/io/dapr/runtime/Dapr.java index 28373414a1..bd600a3262 100644 --- a/sdk/src/test/java/io/dapr/runtime/Dapr.java +++ b/sdk/src/test/java/io/dapr/runtime/Dapr.java @@ -5,12 +5,11 @@ package io.dapr.runtime; -import io.dapr.client.domain.CloudEventEnvelope; -import io.dapr.utils.ObjectSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.dapr.client.domain.CloudEvent; import reactor.core.publisher.Mono; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -23,14 +22,14 @@ public final class Dapr implements DaprRuntime { /** - * Singleton instance for this class. + * Serializes and deserializes internal objects. */ - private static volatile DaprRuntime instance; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** - * Serializes and deserializes internal objects. + * Singleton instance for this class. */ - private final ObjectSerializer serializer = new ObjectSerializer(); + private static volatile DaprRuntime instance; /** * Topics, methods and binding handles. @@ -65,7 +64,7 @@ public static DaprRuntime getInstance() { */ @Override public String serializeSubscribedTopicList() throws IOException { - return new String(this.serializer.serialize(this.getSubscribedTopics()), StandardCharsets.UTF_8); + return OBJECT_MAPPER.writeValueAsString(this.getSubscribedTopics()); } /** @@ -85,7 +84,7 @@ public Collection getSubscribedTopics() { */ @Override public void subscribeToTopic(String topic, TopicListener listener) { - this.handlers.putIfAbsent(topic, new TopicHandler(this.serializer, listener)); + this.handlers.putIfAbsent(topic, new TopicHandler(listener)); } /** @@ -204,18 +203,11 @@ private static final class TopicHandler implements Function apply(HandleRequest r) { try { - CloudEventEnvelope message = this.serializer.deserialize(r.payload, CloudEventEnvelope.class); + CloudEvent message = CloudEvent.deserialize(r.payload); if (message == null) { return Mono.empty(); } diff --git a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java index 9741293980..b3039dec6c 100644 --- a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java +++ b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java @@ -7,10 +7,8 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; -import io.dapr.client.DaprClient; -import io.dapr.client.DaprClientTestBuilder; -import io.dapr.client.DaprHttpStub; -import io.dapr.client.domain.CloudEventEnvelope; +import io.dapr.client.*; +import io.dapr.client.domain.CloudEvent; import io.dapr.client.domain.Verb; import io.dapr.utils.Constants; import org.junit.Assert; @@ -21,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; import java.util.UUID; @@ -72,7 +69,7 @@ public void pubSubHappyCase() throws Exception { new Message( generateMessageId(), TYPE_PLAIN_TEXT, - new byte[0], + "", generateSingleMetadata()), new Message( generateMessageId(), @@ -108,13 +105,14 @@ public void pubSubHappyCase() throws Exception { DaprHttpStub daprHttp = mock(DaprHttpStub.class); DaprClient client = DaprClientTestBuilder.buildHttpClient(daprHttp); + DaprObjectSerializer serializer = new DefaultObjectSerializer(); for (Message message : messages) { when(daprHttp.invokeAPI( eq("POST"), eq(Constants.PUBLISH_PATH + "/" + TOPIC_NAME), eq(null), - eq(message.data), + eq(serializer.serialize(message.data)), eq(null))) .thenAnswer(invocationOnMock -> this.daprRuntime.handleInvocation( TOPIC_NAME, @@ -123,7 +121,7 @@ public void pubSubHappyCase() throws Exception { client.publishEvent(TOPIC_NAME, message.data).block(); - CloudEventEnvelope envelope = new CloudEventEnvelope( + CloudEvent envelope = new CloudEvent( message.id, null, null, @@ -154,7 +152,7 @@ public void invokeHappyCase() throws Exception { new Message( generateMessageId(), TYPE_PLAIN_TEXT, - new byte[0], + "", generateSingleMetadata()), new Message( generateMessageId(), @@ -191,31 +189,30 @@ public void invokeHappyCase() throws Exception { DaprHttpStub daprHttp = mock(DaprHttpStub.class); DaprClient client = DaprClientTestBuilder.buildHttpClient(daprHttp); + DaprObjectSerializer serializer = new DefaultObjectSerializer(); for (Message message : messages) { - byte[] expectedResponse = message.id == null ? new byte[0] : message.id.getBytes(StandardCharsets.UTF_8); - when(listener.process(eq(message.data), eq(message.metadata))) - .then(x -> Mono.just(expectedResponse)); + byte[] expectedResponse = serializer.serialize(message.id); + when(listener.process(eq(serializer.serialize(message.data)), eq(message.metadata))) + .then(x -> expectedResponse == null ? Mono.empty() : Mono.just(expectedResponse)); when(daprHttp.invokeAPI( eq("POST"), eq(Constants.INVOKE_PATH + "/" + APP_ID + "/method/" + METHOD_NAME), eq(null), - eq(message.data), + eq(serializer.serialize(message.data)), any())) .thenAnswer(x -> this.daprRuntime.handleInvocation( METHOD_NAME, - message.data, + serializer.serialize(message.data), message.metadata) .map(r -> new DaprHttpStub.ResponseStub(r, null, 200))); - Mono response = client.invokeService(Verb.POST, APP_ID, METHOD_NAME, message.data, message.metadata); - Assert.assertEquals( - new String(expectedResponse, StandardCharsets.UTF_8), - new String(response.block(), StandardCharsets.UTF_8)); + Mono response = client.invokeService(Verb.POST, APP_ID, METHOD_NAME, message.data, message.metadata, byte[].class); + Assert.assertEquals(expectedResponse, response.block()); verify(listener, times(1)) - .process(eq(message.data), eq(message.metadata)); + .process(eq(serializer.serialize(message.data)), eq(message.metadata)); } verify(listener, times(messages.length)).process(any(), any()); @@ -241,7 +238,7 @@ public void subscribeCallbackException() throws Exception { Mono result = this.daprRuntime .handleInvocation(TOPIC_NAME, this.serialize(message), message.metadata); - CloudEventEnvelope envelope = new CloudEventEnvelope( + CloudEvent envelope = new CloudEvent( message.id, null, null, @@ -280,8 +277,8 @@ private static final String generateMessageId() { return UUID.randomUUID().toString(); } - private static final byte[] generatePayload() { - return UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8); + private static final String generatePayload() { + return UUID.randomUUID().toString(); } private static final Map generateSingleMetadata() { @@ -294,11 +291,11 @@ private static final class Message { private final String datacontenttype; - private final byte[] data; + private final String data; private final Map metadata; - private Message(String id, String datacontenttype, byte[] data, Map metadata) { + private Message(String id, String datacontenttype, String data, Map metadata) { this.id = id; this.datacontenttype = datacontenttype; this.data = data; @@ -321,7 +318,7 @@ private byte[] serialize(Message message) throws IOException { generator.writeStringField("datacontenttype", message.datacontenttype); } if (message.data != null) { - generator.writeBinaryField("data", message.data); + generator.writeStringField("data", message.data); } generator.writeEndObject(); generator.close(); diff --git a/sdk/src/test/java/io/dapr/runtime/TopicListener.java b/sdk/src/test/java/io/dapr/runtime/TopicListener.java index ec87133831..eea3a04e2f 100644 --- a/sdk/src/test/java/io/dapr/runtime/TopicListener.java +++ b/sdk/src/test/java/io/dapr/runtime/TopicListener.java @@ -5,7 +5,7 @@ package io.dapr.runtime; -import io.dapr.client.domain.CloudEventEnvelope; +import io.dapr.client.domain.CloudEvent; import reactor.core.publisher.Mono; import java.util.Map; @@ -22,6 +22,6 @@ public interface TopicListener { * @return Empty response. * @throws Exception Any exception from user code. */ - Mono process(CloudEventEnvelope message, Map metadata) throws Exception; + Mono process(CloudEvent message, Map metadata) throws Exception; } diff --git a/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java b/sdk/src/test/java/io/dapr/utils/DefaultObjectSerializerTest.java similarity index 70% rename from sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java rename to sdk/src/test/java/io/dapr/utils/DefaultObjectSerializerTest.java index ae20dc1e88..4e61d641ef 100644 --- a/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java +++ b/sdk/src/test/java/io/dapr/utils/DefaultObjectSerializerTest.java @@ -5,15 +5,23 @@ package io.dapr.utils; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.dapr.client.DefaultObjectSerializer; +import io.dapr.client.domain.CloudEvent; +import org.junit.Assert; import org.junit.Test; import java.io.IOException; import java.io.Serializable; +import java.util.Base64; +import java.util.function.Function; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; -public class ObjectSerializerTest { +public class DefaultObjectSerializerTest { + + private static final DefaultObjectSerializer SERIALIZER = new DefaultObjectSerializer(); public static class MyObjectTestToSerialize implements Serializable { private String stringValue; @@ -187,11 +195,11 @@ public void serializeStringObjectTest() { obj.setDoubleValue(1000.0); String expectedResult = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; try { - serializedValue = serializer.serializeString(obj); - assertEquals("FOUND:[[" + serializedValue + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, serializedValue); + serializedValue = new String(SERIALIZER.serialize(obj)); + assertEquals("FOUND:[[" + serializedValue + "]] \n but was EXPECTING: [[" + expectedResult + "]]", expectedResult, serializedValue); } catch (IOException exception) { fail(exception.getMessage()); } @@ -211,12 +219,11 @@ public void serializeObjectTest() { obj.setDoubleValue(1000.0); //String expectedResult = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); byte[] serializedValue; try { - serializedValue = serializer.serialize(obj); + serializedValue = SERIALIZER.serialize(obj); assertNotNull(serializedValue); - MyObjectTestToSerialize deserializedValue = serializer.deserialize(serializedValue, MyObjectTestToSerialize.class); + MyObjectTestToSerialize deserializedValue = SERIALIZER.deserialize(serializedValue, MyObjectTestToSerialize.class); assertEquals(obj, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -225,13 +232,10 @@ public void serializeObjectTest() { @Test public void serializeNullTest() { - ObjectSerializer serializer = new ObjectSerializer(); - String serializedValue; + byte[] byteSerializedValue; try { - serializedValue = serializer.serializeString(null); - assertNull(serializedValue); - byteSerializedValue = serializer.serialize(null); + byteSerializedValue = SERIALIZER.serialize(null); assertNull(byteSerializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -241,15 +245,15 @@ public void serializeNullTest() { @Test public void serializeStringTest() { String valueToSerialize = "A String"; - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize); - assertEquals(valueToSerialize, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize)); + assertEquals("\"" + valueToSerialize + "\"", serializedValue); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - String deserializedValue = serializer.deserialize(byteValue, String.class); + String deserializedValue = SERIALIZER.deserialize(byteValue, String.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -260,15 +264,15 @@ public void serializeStringTest() { public void serializeIntTest() { Integer valueToSerialize = 1; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.intValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.intValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Integer deserializedValue = serializer.deserialize(byteValue, Integer.class); + Integer deserializedValue = SERIALIZER.deserialize(byteValue, Integer.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -279,15 +283,15 @@ public void serializeIntTest() { public void serializeShortTest() { Short valueToSerialize = 1; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.shortValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.shortValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Short deserializedValue = serializer.deserialize(byteValue, Short.class); + Short deserializedValue = SERIALIZER.deserialize(byteValue, Short.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -296,17 +300,17 @@ public void serializeShortTest() { @Test public void serializeLongTest() { - Long valueToSerialize = 1L; + Long valueToSerialize = Long.MAX_VALUE; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.longValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.longValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Long deserializedValue = serializer.deserialize(byteValue, Long.class); + Long deserializedValue = SERIALIZER.deserialize(byteValue, Long.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -315,18 +319,18 @@ public void serializeLongTest() { @Test public void serializeFloatTest() { - Float valueToSerialize = 1.0f; + Float valueToSerialize = -1.23456f; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.floatValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.floatValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Float deserializedValue = serializer.deserialize(byteValue, Float.class); - assertEquals(valueToSerialize, deserializedValue); + Float deserializedValue = SERIALIZER.deserialize(byteValue, Float.class); + assertEquals(valueToSerialize, deserializedValue, 0.00000000001); } catch (IOException exception) { fail(exception.getMessage()); } @@ -336,15 +340,15 @@ public void serializeFloatTest() { public void serializeDoubleTest() { Double valueToSerialize = 1.0; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.doubleValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.doubleValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Double deserializedValue = serializer.deserialize(byteValue, Double.class); + Double deserializedValue = SERIALIZER.deserialize(byteValue, Double.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -355,15 +359,15 @@ public void serializeDoubleTest() { public void serializeBooleanTest() { Boolean valueToSerialize = true; String expectedResult = valueToSerialize.toString(); - ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; byte [] byteValue; try { - serializedValue = serializer.serializeString(valueToSerialize.booleanValue()); + serializedValue = new String(SERIALIZER.serialize(valueToSerialize.booleanValue())); assertEquals(expectedResult, serializedValue); - byteValue = serializer.serialize(valueToSerialize); + byteValue = SERIALIZER.serialize(valueToSerialize); assertNotNull(byteValue); - Boolean deserializedValue = serializer.deserialize(byteValue, Boolean.class); + Boolean deserializedValue = SERIALIZER.deserialize(byteValue, Boolean.class); assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); @@ -384,9 +388,9 @@ public void deserializeObjectTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("The expected value is different than the actual result", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -394,13 +398,12 @@ public void deserializeObjectTest() { } @Test - public void deserializeBtyesTest() { - ObjectSerializer serializer = new ObjectSerializer(); + public void deserializeBytesTest() { + try { - byte[] resultStr = serializer.deserialize("String", byte[].class); - assertNotNull(resultStr); - byte[] result = serializer.deserialize("String".getBytes(), byte[].class); + byte[] result = SERIALIZER.deserialize("String".getBytes(), byte[].class); assertNotNull(result); + assertEquals("String", new String(result)); } catch (IOException exception) { fail(exception.getMessage()); } @@ -408,31 +411,31 @@ public void deserializeBtyesTest() { @Test public void deserializeNullObjectOrPrimitiveTest() { - ObjectSerializer serializer = new ObjectSerializer(); + try { MyObjectTestToSerialize expectedObj = null; - MyObjectTestToSerialize objResult = serializer.deserialize(null, MyObjectTestToSerialize.class); + MyObjectTestToSerialize objResult = SERIALIZER.deserialize(null, MyObjectTestToSerialize.class); assertEquals(expectedObj, objResult); boolean expectedBoolResutl = false; - boolean boolResult = serializer.deserialize(null, boolean.class); + boolean boolResult = SERIALIZER.deserialize(null, boolean.class); assertEquals(expectedBoolResutl, boolResult); byte expectedByteResult = Byte.valueOf((byte) 0); - byte byteResult = serializer.deserialize(null, byte.class); + byte byteResult = SERIALIZER.deserialize(null, byte.class); assertEquals(expectedByteResult, byteResult); short expectedShortResult = (short) 0; - short shortResult = serializer.deserialize(null, short.class); + short shortResult = SERIALIZER.deserialize(null, short.class); assertEquals(expectedShortResult, shortResult); int expectedIntResult = 0; - int intResult = serializer.deserialize(null, int.class); + int intResult = SERIALIZER.deserialize(null, int.class); assertEquals(expectedIntResult, intResult); long expectedLongResult = 0L; - long longResult = serializer.deserialize(null, long.class); + long longResult = SERIALIZER.deserialize(null, long.class); assertEquals(expectedLongResult, longResult); float expectedFloatResult = 0f; - float floatResult = serializer.deserialize(null, float.class); + float floatResult = SERIALIZER.deserialize(null, float.class); assertEquals(expectedFloatResult, floatResult, 0.0f); double expectedDoubleResult = (double) 0; - double doubleResult = serializer.deserialize(null, double.class); + double doubleResult = SERIALIZER.deserialize(null, double.class); assertEquals(expectedDoubleResult, doubleResult, 0.0); } catch (IOException exception) { fail(exception.getMessage()); @@ -452,9 +455,9 @@ public void deserializeObjectMissingStringPropertyTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -474,9 +477,9 @@ public void deserializeObjectMissingIntTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -496,9 +499,9 @@ public void deserializeObjectMissingBooleanTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -518,9 +521,9 @@ public void deserializeObjectMissingCharTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -540,9 +543,9 @@ public void deserializeObjectMissingByteTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -562,9 +565,9 @@ public void deserializeObjectMissingShortTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -584,9 +587,9 @@ public void deserializeObjectMissingLongTest() { expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -606,9 +609,9 @@ public void deserializeObjectMissingFloatTest() { expectedResult.setLongValue(9223372036854775807L); expectedResult.setDoubleValue(1000.0); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -628,9 +631,9 @@ public void deserializeObjectMissingDoubleTest() { expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); MyObjectTestToSerialize result; - ObjectSerializer serializer = new ObjectSerializer(); + try { - result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); assertEquals("FOUND:[[" + result + "]] \n but was EXPECING: [[" + expectedResult + "]]", expectedResult, result); } catch (IOException exception) { fail(exception.getMessage()); @@ -640,16 +643,16 @@ public void deserializeObjectMissingDoubleTest() { @Test(expected = IOException.class) public void deserializeObjectIntExceedMaximunValueTest() throws Exception { String jsonToDeserialize = "{\"stringValue\":\"A String\",\"intValue\":2147483648,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); - serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + + SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); } @Test(expected = IOException.class) public void deserializeObjectNotACharTest() throws Exception { String jsonToDeserialize = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"Not A Char\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); + try { - serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); } catch (IOException ioEx) { throw ioEx; } catch (Exception ex) { @@ -660,14 +663,132 @@ public void deserializeObjectNotACharTest() throws Exception { @Test(expected = IOException.class) public void deserializeObjectShortExceededMaximunValueTest() throws Exception { String jsonToDeserialize = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32768,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); - serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + + SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); } @Test(expected = IOException.class) public void deserializeObjectLongExceededMaximumValueTest() throws Exception { String jsonToDeserialize = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775808,\"floatValue\":1.0,\"doubleValue\":1000.0}"; - ObjectSerializer serializer = new ObjectSerializer(); - MyObjectTestToSerialize result = serializer.deserialize(jsonToDeserialize, MyObjectTestToSerialize.class); + + MyObjectTestToSerialize result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class); + } + + @Test + public void deserializeNullToPrimitives() throws Exception { + + assertEquals(0, (char)SERIALIZER.deserialize(null, char.class)); + assertEquals(0, (int)SERIALIZER.deserialize(null, int.class)); + assertEquals(0, (long)SERIALIZER.deserialize(null, long.class)); + assertEquals(0, (byte)SERIALIZER.deserialize(null, byte.class)); + assertEquals(0, SERIALIZER.deserialize(null, double.class), 0); + assertEquals(0, SERIALIZER.deserialize(null, float.class), 0); + assertEquals(false, SERIALIZER.deserialize(null, boolean.class)); + + assertNull(SERIALIZER.deserialize(null, Character.class)); + assertNull(SERIALIZER.deserialize(null, Integer.class)); + assertNull(SERIALIZER.deserialize(null, Long.class)); + assertNull(SERIALIZER.deserialize(null, Byte.class)); + assertNull(SERIALIZER.deserialize(null, Double.class)); + assertNull(SERIALIZER.deserialize(null, Float.class)); + assertNull(SERIALIZER.deserialize(null, Boolean.class)); + } + + @Test + public void deserializeEmptyByteArrayToPrimitives() throws Exception { + + assertEquals(0, (char)SERIALIZER.deserialize(new byte[0], char.class)); + assertEquals(0, (int)SERIALIZER.deserialize(new byte[0], int.class)); + assertEquals(0, (long)SERIALIZER.deserialize(new byte[0], long.class)); + assertEquals(0, (byte)SERIALIZER.deserialize(new byte[0], byte.class)); + assertEquals(0, SERIALIZER.deserialize(new byte[0], double.class), 0); + assertEquals(0, SERIALIZER.deserialize(new byte[0], float.class), 0); + assertEquals(false, SERIALIZER.deserialize(new byte[0], boolean.class)); + + assertNull(SERIALIZER.deserialize(new byte[0], Character.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Integer.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Long.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Byte.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Double.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Float.class)); + assertNull(SERIALIZER.deserialize(new byte[0], Boolean.class)); + } + + @Test + public void serializeDeserializeCloudEventEnvelope() throws Exception { + + + Function check = (e -> { + try { + if (e == null) { + return CloudEvent.deserialize(SERIALIZER.serialize(e)) == null; + } + + return e.equals(CloudEvent.deserialize(SERIALIZER.serialize(e))); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + + Assert.assertTrue(check.apply(null)); + Assert.assertTrue(check.apply( + new CloudEvent( + "1", + "mysource", + "text", + "v2", + "XML", + ""))); + Assert.assertTrue(check.apply( + new CloudEvent( + "1234-65432", + "myother", + "image", + "v2", + "byte", + Base64.getEncoder().encodeToString(new byte[] {0, 2, 99})))); + } + + @Test + public void deserializeCloudEventEnvelopeData() throws Exception { + + + Function deserializeData = (jsonData -> { + try { + String payload = String.format("{\"data\": %s}", jsonData); + return CloudEvent.deserialize(payload.getBytes()).getData(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + + assertEquals("123", + deserializeData.apply("123")); + assertEquals("true", + deserializeData.apply("true")); + assertEquals("123.45", + deserializeData.apply("123.45")); + assertEquals("AAEI", + deserializeData.apply(quote(Base64.getEncoder().encodeToString(new byte[] { 0, 1, 8})))); + assertEquals("hello world", + deserializeData.apply(quote("hello world"))); + assertEquals("\"hello world\"", + deserializeData.apply(quote("\\\"hello world\\\""))); + assertEquals("\"hello world\"", + deserializeData.apply(new ObjectMapper().writeValueAsString("\"hello world\""))); + assertEquals("hello world", + deserializeData.apply(new ObjectMapper().writeValueAsString("hello world"))); + assertEquals("{\"id\":\"123:\",\"name\":\"Jon Doe\"}", + deserializeData.apply("{\"id\": \"123:\", \"name\": \"Jon Doe\"}")); + assertEquals("{\"id\": \"123:\", \"name\": \"Jon Doe\"}", + deserializeData.apply(new ObjectMapper().writeValueAsString("{\"id\": \"123:\", \"name\": \"Jon Doe\"}"))); + } + + private static String quote(String content) { + if (content == null) { + return null; + } + + return "\"" + content + "\""; } } diff --git a/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java b/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java index cf3bc057c9..2cfc5b83b2 100644 --- a/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java +++ b/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java @@ -1,6 +1,5 @@ package io.dapr.utils; -import io.dapr.utils.DurationUtils; import org.junit.Assert; import org.junit.Test;