Use TypeRef for deserialization and client APIs. (#301)

* Use TypeRef for deserialization and client APIs.

* Making TypeRef class constructor private to force use of .get()
This commit is contained in:
Artur Souza 2020-07-09 11:47:25 -07:00 committed by GitHub
parent f020b33656
commit bd4b24cdb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 706 additions and 140 deletions

View File

@ -9,6 +9,7 @@ import io.dapr.actors.ActorId;
import io.dapr.actors.runtime.AbstractActor;
import io.dapr.actors.runtime.ActorRuntimeContext;
import io.dapr.actors.runtime.Remindable;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.text.DateFormat;
@ -121,8 +122,8 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
* @return Class for reminder's state.
*/
@Override
public Class<Integer> getStateType() {
return Integer.class;
public TypeRef<Integer> getStateType() {
return TypeRef.INT;
}
/**

View File

@ -6,6 +6,7 @@
package io.dapr.actors.client;
import io.dapr.actors.ActorId;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
/**
@ -27,6 +28,16 @@ public interface ActorProxy {
*/
String getActorType();
/**
* Invokes an Actor method on Dapr.
*
* @param methodName Method name to invoke.
* @param type The type of the return class.
* @param <T> The type to be returned.
* @return Asynchronous result with the Actor's response.
*/
<T> Mono<T> invokeActorMethod(String methodName, TypeRef<T> type);
/**
* Invokes an Actor method on Dapr.
*
@ -37,6 +48,17 @@ public interface ActorProxy {
*/
<T> Mono<T> invokeActorMethod(String methodName, Class<T> clazz);
/**
* Invokes an Actor method on Dapr.
*
* @param methodName Method name to invoke.
* @param data Object with the data.
* @param type The type of the return class.
* @param <T> The type to be returned.
* @return Asynchronous result with the Actor's response.
*/
<T> Mono<T> invokeActorMethod(String methodName, Object data, TypeRef<T> type);
/**
* Invokes an Actor method on Dapr.
*

View File

@ -8,6 +8,7 @@ package io.dapr.actors.client;
import io.dapr.actors.ActorId;
import io.dapr.actors.ActorMethod;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -72,10 +73,28 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeActorMethod(String methodName, Object data, Class<T> clazz) {
public <T> Mono<T> invokeActorMethod(String methodName, Object data, TypeRef<T> type) {
return this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, this.serialize(data))
.filter(s -> s.length > 0)
.map(s -> deserialize(s, clazz));
.map(s -> deserialize(s, type));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeActorMethod(String methodName, Object data, Class<T> clazz) {
return this.invokeActorMethod(methodName, data, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeActorMethod(String methodName, TypeRef<T> type) {
return this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null)
.filter(s -> s.length > 0)
.map(s -> deserialize(s, type));
}
/**
@ -83,9 +102,7 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
*/
@Override
public <T> Mono<T> invokeActorMethod(String methodName, Class<T> clazz) {
return this.daprClient.invokeActorMethod(actorType, actorId.toString(), methodName, null)
.filter(s -> s.length > 0)
.map(s -> deserialize(s, clazz));
return this.invokeActorMethod(methodName, TypeRef.get(clazz));
}
/**
@ -147,14 +164,14 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
* Extracts the response object from the Actor's method result.
*
* @param response response returned by API.
* @param clazz Expected response class.
* @param type Expected response type.
* @param <T> Expected response type.
* @return Response object or null.
* @throws RuntimeException In case it cannot generate Object.
*/
private <T> T deserialize(final byte[] response, Class<T> clazz) {
private <T> T deserialize(final byte[] response, TypeRef<T> type) {
try {
return this.serializer.deserialize(response, clazz);
return this.serializer.deserialize(response, type);
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@ -6,6 +6,7 @@
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -253,7 +254,7 @@ class ActorManager<T extends AbstractActor> {
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);
input = this.runtimeContext.getObjectSerializer().deserialize(request, TypeRef.get(inputClass));
}
if (method.getReturnType().equals(Mono.class)) {

View File

@ -6,6 +6,7 @@
package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
@ -101,6 +102,18 @@ public class ActorStateManager {
* @return Asynchronous response with fetched object.
*/
public <T> Mono<T> get(String stateName, Class<T> clazz) {
return this.get(stateName, TypeRef.get(clazz));
}
/**
* Fetches the most recent value for the given state, including cached value.
*
* @param stateName Name of the state.
* @param type Class type for the value being fetched.
* @param <T> Type being fetched.
* @return Asynchronous response with fetched object.
*/
public <T> Mono<T> get(String stateName, TypeRef<T> type) {
return Mono.fromSupplier(() -> {
if (stateName == null) {
throw new IllegalArgumentException("State's name cannot be null.");
@ -118,7 +131,7 @@ public class ActorStateManager {
return (T) null;
}).switchIfEmpty(
this.stateProvider.load(this.actorTypeName, this.actorId, stateName, clazz)
this.stateProvider.load(this.actorTypeName, this.actorId, stateName, type)
.switchIfEmpty(Mono.error(new NoSuchElementException("State not found: " + stateName)))
.map(v -> {
this.stateChangeTracker.put(stateName, new StateChangeMetadata(ActorStateChangeKind.NONE, v));

View File

@ -11,6 +11,7 @@ import io.dapr.actors.ActorId;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.Properties;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.ByteArrayOutputStream;
@ -59,12 +60,12 @@ class DaprStateAsyncProvider {
this.isStateSerializerDefault = stateSerializer.getClass() == DefaultObjectSerializer.class;
}
<T> Mono<T> load(String actorType, ActorId actorId, String stateName, Class<T> clazz) {
<T> Mono<T> load(String actorType, ActorId actorId, String stateName, TypeRef<T> type) {
Mono<byte[]> result = this.daprClient.getActorState(actorType, actorId.toString(), stateName);
return result.flatMap(s -> {
try {
T response = this.stateSerializer.deserialize(s, clazz);
T response = this.stateSerializer.deserialize(s, type);
if (response == null) {
return Mono.empty();
}

View File

@ -5,6 +5,7 @@
package io.dapr.actors.runtime;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.time.Duration;
@ -15,11 +16,11 @@ import java.time.Duration;
public interface Remindable<T> {
/**
* Gets the class for state object.
* Gets the type for state object.
*
* @return Class for state object.
*/
Class<T> getStateType();
TypeRef<T> getStateType();
/**
* The reminder call back invoked when an actor reminder is triggered.

View File

@ -11,6 +11,8 @@ import io.dapr.serializer.DefaultObjectSerializer;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import io.dapr.utils.TypeRef;
import org.junit.Assert;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
@ -96,8 +98,8 @@ public class ActorManagerTest {
}
@Override
public Class<String> getStateType() {
return String.class;
public TypeRef<String> getStateType() {
return TypeRef.STRING;
}
@Override
@ -124,8 +126,8 @@ public class ActorManagerTest {
this.manager.activateActor(actorId).block();
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.context.getObjectSerializer().deserialize(message, TypeRef.STRING)),
this.context.getObjectSerializer().deserialize(response, TypeRef.STRING));
}
@Test
@ -192,8 +194,8 @@ public class ActorManagerTest {
this.manager.activateActor(actorId).block();
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.context.getObjectSerializer().deserialize(message, TypeRef.STRING)),
this.context.getObjectSerializer().deserialize(response, TypeRef.STRING));
this.manager.deactivateActor(actorId).block();
this.manager.invokeMethod(actorId, "say", message).block();

View File

@ -11,6 +11,7 @@ import io.dapr.actors.client.ActorProxy;
import io.dapr.actors.client.ActorProxyForTestsImpl;
import io.dapr.actors.client.DaprClientStub;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.TypeRef;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
@ -247,9 +248,9 @@ public class ActorStatefulTest {
}
@Override
public Class<String> getStateType() {
public TypeRef<String> getStateType() {
// Remindable type.
return String.class;
return TypeRef.STRING;
}
@Override

View File

@ -6,6 +6,7 @@
package io.dapr.actors.runtime;
import io.dapr.actors.ActorType;
import io.dapr.utils.TypeRef;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
@ -64,7 +65,7 @@ public class ActorTypeInformationTest {
}
@Override
public Class getStateType() {
public TypeRef getStateType() {
return null;
}

View File

@ -7,6 +7,7 @@ package io.dapr.actors.runtime;
import io.dapr.actors.ActorId;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -28,7 +29,7 @@ public class DaprInMemoryStateProvider extends DaprStateAsyncProvider {
}
@Override
<T> Mono<T> load(String actorType, ActorId actorId, String stateName, Class<T> clazz) {
<T> Mono<T> load(String actorType, ActorId actorId, String stateName, TypeRef<T> type) {
return Mono.fromSupplier(() -> {
try {
String stateId = this.buildId(actorType, actorId, stateName);
@ -36,7 +37,7 @@ public class DaprInMemoryStateProvider extends DaprStateAsyncProvider {
throw new IllegalStateException("State not found.");
}
return this.serializer.deserialize(this.stateStore.get(stateId), clazz);
return this.serializer.deserialize(this.stateStore.get(stateId), type);
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.actors.ActorId;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.TypeRef;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
@ -170,23 +171,24 @@ public class DaprStateAsyncProviderTest {
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, SERIALIZER);
Assert.assertEquals("Jon Doe",
provider.load("MyActor", new ActorId("123"), "name", String.class).block());
provider.load("MyActor", new ActorId("123"), "name", TypeRef.STRING).block());
Assert.assertEquals(98021,
(int)provider.load("MyActor", new ActorId("123"), "zipcode", int.class).block());
(int)provider.load("MyActor", new ActorId("123"), "zipcode", TypeRef.INT).block());
Assert.assertEquals(98,
(int) provider.load("MyActor", new ActorId("123"), "goals", int.class).block());
(int) provider.load("MyActor", new ActorId("123"), "goals", TypeRef.INT).block());
Assert.assertEquals(98,
(int) provider.load("MyActor", new ActorId("123"), "goals", int.class).block());
(int) provider.load("MyActor", new ActorId("123"), "goals", TypeRef.INT).block());
Assert.assertEquals(46.55,
(double) provider.load("MyActor", new ActorId("123"), "balance", double.class).block(),
(double) provider.load("MyActor", new ActorId("123"), "balance", TypeRef.DOUBLE).block(),
EPSILON);
Assert.assertEquals(true,
(boolean) provider.load("MyActor", new ActorId("123"), "active", boolean.class).block());
(boolean) provider.load("MyActor", new ActorId("123"), "active", TypeRef.BOOLEAN).block());
Assert.assertEquals(new Customer().setId(1000).setName("Roxane"),
provider.load("MyActor", new ActorId("123"), "customer", Customer.class).block());
provider.load("MyActor", new ActorId("123"), "customer", TypeRef.get(Customer.class)).block());
Assert.assertNotEquals(new Customer().setId(1000).setName("Roxane"),
provider.load("MyActor", new ActorId("123"), "anotherCustomer", Customer.class).block());
Assert.assertNull(provider.load("MyActor", new ActorId("123"), "nullCustomer", Customer.class).block());
provider.load("MyActor", new ActorId("123"), "anotherCustomer", TypeRef.get(Customer.class)).block());
Assert.assertNull(
provider.load("MyActor", new ActorId("123"), "nullCustomer", TypeRef.get(Customer.class)).block());
}
@Test

View File

@ -6,6 +6,7 @@
package io.dapr.actors.runtime;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;
import java.io.*;
@ -32,7 +33,7 @@ public class JavaSerializer implements DaprObjectSerializer {
* {@inheritDoc}
*/
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) throws IOException {
public <T> T deserialize(byte[] data, TypeRef<T> type) throws IOException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
try (ObjectInputStream ois = new ObjectInputStream(bis)) {
try {

View File

@ -11,6 +11,7 @@ import io.dapr.actors.runtime.AbstractActor;
import io.dapr.actors.runtime.ActorRuntimeContext;
import io.dapr.actors.runtime.Remindable;
import io.dapr.it.actors.MethodEntryTracker;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.text.DateFormat;
@ -146,8 +147,8 @@ public class MyActorImpl extends AbstractActor implements MyActor, Remindable<St
}
@Override
public Class<String> getStateType() {
return String.class;
public TypeRef<String> getStateType() {
return TypeRef.STRING;
}

View File

@ -5,12 +5,10 @@
package io.dapr.client;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.ByteString;
import io.dapr.client.domain.State;
import io.dapr.client.domain.StateOptions;
import io.dapr.client.domain.Verb;
import io.dapr.v1.DaprProtos;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.util.List;
@ -45,14 +43,29 @@ public interface DaprClient {
/**
* Invoke a service with all possible parameters, using serialization.
*
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param request The request to be sent to invoke the service, use byte[] to skip serialization.
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param request The request to be sent to invoke the service, use byte[] to skip serialization.
* @param metadata Metadata (in GRPC) or headers (in HTTP) to be send in request.
* @param clazz the Type needed as return for the call.
* @param <T> the Type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type clazz.
* @param type The Type needed as return for the call.
* @param <T> The Type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type type .
*/
<T> Mono<T> invokeService(
Verb verb, String appId, String method, Object request, Map<String, String> metadata, TypeRef<T> type);
/**
* Invoke a service with all possible parameters, using serialization.
*
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param request The request to be sent to invoke the service, use byte[] to skip serialization.
* @param metadata Metadata (in GRPC) or headers (in HTTP) to be send in request.
* @param clazz The type needed as return for the call.
* @param <T> The type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type type .
*/
<T> Mono<T> invokeService(
Verb verb, String appId, String method, Object request, Map<String, String> metadata, Class<T> clazz);
@ -64,22 +77,48 @@ public interface DaprClient {
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param request The request to be sent to invoke the service, use byte[] to skip serialization.
* @param clazz the Type needed as return for the call.
* @param <T> the Type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type clazz.
* @param type The type needed as return for the call.
* @param <T> The type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type type .
*/
<T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, TypeRef<T> type);
/**
* Invoke a service without metadata, using serialization.
*
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param request The request to be sent to invoke the service, use byte[] to skip serialization.
* @param clazz The type needed as return for the call.
* @param <T> The type of the return, use byte[] to skip serialization.
* @return A Mono Plan of type type .
*/
<T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, Class<T> clazz);
/**
* Invoke a service without input, using serialization for response.
*
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param metadata Metadata (in GRPC) or headers (in HTTP) to be send in request.
* @param clazz the Type needed as return for the call.
* @param <T> the Type of the return, use byte[] to skip serialization.
* @return A Mono plan of type clazz.
* @param type The type needed as return for the call.
* @param <T> The type of the return, use byte[] to skip serialization.
* @return A Mono plan of type type .
*/
<T> Mono<T> invokeService(Verb verb, String appId, String method, Map<String, String> metadata, TypeRef<T> type);
/**
* Invoke a service without input, using serialization for response.
*
* @param verb The Verb to be used for HTTP will be the HTTP Verb, for GRPC is just a metadata value.
* @param appId The Application ID where the service is.
* @param method The actual Method to be call in the application.
* @param metadata Metadata (in GRPC) or headers (in HTTP) to be send in request.
* @param clazz The type needed as return for the call.
* @param <T> The type of the return, use byte[] to skip serialization.
* @return A Mono plan of type type .
*/
<T> Mono<T> invokeService(Verb verb, String appId, String method, Map<String, String> metadata, Class<T> clazz);
@ -150,6 +189,18 @@ public interface DaprClient {
*/
Mono<byte[]> invokeBinding(String name, String operation, byte[] data, Map<String, String> metadata);
/**
* Invokes a Binding operation.
*
* @param name The name of the biding to call.
* @param operation The operation to be performed by the binding request processor.
* @param data The data to be processed, use byte[] to skip serialization.
* @param type The type being returned.
* @param <T> The type of the return
* @return a Mono plan of type T.
*/
<T> Mono<T> invokeBinding(String name, String operation, Object data, TypeRef<T> type);
/**
* Invokes a Binding operation.
*
@ -162,6 +213,19 @@ public interface DaprClient {
*/
<T> Mono<T> invokeBinding(String name, String operation, Object data, Class<T> clazz);
/**
* Invokes a Binding operation.
*
* @param name The name of the biding to call.
* @param operation The operation to be performed by the binding request processor.
* @param data The data to be processed, use byte[] to skip serialization.
* @param metadata The metadata map.
* @param type The type being returned.
* @param <T> The type of the return
* @return a Mono plan of type T.
*/
<T> Mono<T> invokeBinding(String name, String operation, Object data, Map<String, String> metadata, TypeRef<T> type);
/**
* Invokes a Binding operation.
*
@ -180,8 +244,19 @@ public interface DaprClient {
*
* @param stateStoreName The name of the state store.
* @param state State to be re-retrieved.
* @param clazz The Type of State needed as return.
* @param <T> The Type of the return.
* @param type The type of State needed as return.
* @param <T> The type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, State<T> state, TypeRef<T> type);
/**
* Retrieve a State based on their key.
*
* @param stateStoreName The name of the state store.
* @param state State to be re-retrieved.
* @param clazz The type of State needed as return.
* @param <T> The type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, State<T> state, Class<T> clazz);
@ -191,8 +266,19 @@ public interface DaprClient {
*
* @param stateStoreName The name of the state store.
* @param key The key of the State to be retrieved.
* @param clazz The Type of State needed as return.
* @param <T> The Type of the return.
* @param type The type of State needed as return.
* @param <T> The type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, String key, TypeRef<T> type);
/**
* Retrieve a State based on their key.
*
* @param stateStoreName The name of the state store.
* @param key The key of the State to be retrieved.
* @param clazz The type of State needed as return.
* @param <T> The type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, String key, Class<T> clazz);
@ -204,10 +290,23 @@ public interface DaprClient {
* @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 type The Type of State needed as return.
* @param <T> The Type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, String key, String etag, StateOptions options, TypeRef<T> type);
/**
* Retrieve a State based on their key.
*
* @param stateStoreName The name of the state store.
* @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 <T> The type of the return.
* @return A Mono Plan for the requested State.
*/
<T> Mono<State<T>> getState(String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz);
/**

View File

@ -14,6 +14,7 @@ import io.dapr.client.domain.State;
import io.dapr.client.domain.StateOptions;
import io.dapr.client.domain.Verb;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;
import io.dapr.v1.CommonProtos;
import io.dapr.v1.DaprGrpc;
import io.dapr.v1.DaprProtos;
@ -139,27 +140,58 @@ public class DaprClientGrpc implements DaprClient {
String method,
Object request,
Map<String, String> metadata,
Class<T> clazz) {
TypeRef<T> type) {
try {
DaprProtos.InvokeServiceRequest envelope = buildInvokeServiceRequest(verb.toString(), appId, method, request);
return Mono.fromCallable(() -> {
ListenableFuture<CommonProtos.InvokeResponse> futureResponse =
client.invokeService(envelope);
return objectSerializer.deserialize(futureResponse.get().getData().getValue().toByteArray(), clazz);
return objectSerializer.deserialize(futureResponse.get().getData().getValue().toByteArray(), type);
});
} catch (Exception ex) {
return Mono.error(ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb,
String appId,
String method,
Object request,
Map<String, String> metadata,
Class<T> clazz) {
return this.invokeService(verb, appId, method, request, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata, TypeRef<T> type) {
return this.invokeService(verb, appId, method, null, metadata, type);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata, Class<T> clazz) {
return this.invokeService(verb, appId, method, null, metadata, clazz);
return this.invokeService(verb, appId, method, null, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, TypeRef<T> type) {
return this.invokeService(verb, appId, method, request, null, type);
}
/**
@ -167,7 +199,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public <T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, Class<T> clazz) {
return this.invokeService(verb, appId, method, request, null, clazz);
return this.invokeService(verb, appId, method, request, null, TypeRef.get(clazz));
}
/**
@ -175,7 +207,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public Mono<Void> invokeService(Verb verb, String appId, String method, Object request) {
return this.invokeService(verb, appId, method, request, null, byte[].class).then();
return this.invokeService(verb, appId, method, request, null, TypeRef.BYTE_ARRAY).then();
}
/**
@ -184,7 +216,7 @@ public class DaprClientGrpc implements DaprClient {
@Override
public Mono<Void> invokeService(
Verb verb, String appId, String method, Object request, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, request, metadata, byte[].class).then();
return this.invokeService(verb, appId, method, request, metadata, TypeRef.BYTE_ARRAY).then();
}
/**
@ -193,7 +225,7 @@ public class DaprClientGrpc implements DaprClient {
@Override
public Mono<Void> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, null, metadata, byte[].class).then();
return this.invokeService(verb, appId, method, null, metadata, TypeRef.BYTE_ARRAY).then();
}
/**
@ -202,7 +234,7 @@ public class DaprClientGrpc implements DaprClient {
@Override
public Mono<byte[]> invokeService(
Verb verb, String appId, String method, byte[] request, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, request, metadata, byte[].class);
return this.invokeService(verb, appId, method, request, metadata, TypeRef.BYTE_ARRAY);
}
/**
@ -210,7 +242,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public Mono<Void> invokeBinding(String name, String operation, Object data) {
return this.invokeBinding(name, operation, data, null, byte[].class).then();
return this.invokeBinding(name, operation, data, null, TypeRef.BYTE_ARRAY).then();
}
/**
@ -218,7 +250,15 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public Mono<byte[]> invokeBinding(String name, String operation, byte[] data, Map<String, String> metadata) {
return this.invokeBinding(name, operation, data, metadata, byte[].class);
return this.invokeBinding(name, operation, data, metadata, TypeRef.BYTE_ARRAY);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeBinding(String name, String operation, Object data, TypeRef<T> type) {
return this.invokeBinding(name, operation, data, null, type);
}
/**
@ -226,7 +266,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public <T> Mono<T> invokeBinding(String name, String operation, Object data, Class<T> clazz) {
return this.invokeBinding(name, operation, data, null, clazz);
return this.invokeBinding(name, operation, data, null, TypeRef.get(clazz));
}
/**
@ -234,7 +274,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public <T> Mono<T> invokeBinding(
String name, String operation, Object data, Map<String, String> metadata, Class<T> clazz) {
String name, String operation, Object data, Map<String, String> metadata, TypeRef<T> type) {
try {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Binding name cannot be null or empty.");
@ -256,19 +296,44 @@ public class DaprClientGrpc implements DaprClient {
DaprProtos.InvokeBindingRequest envelope = builder.build();
return Mono.fromCallable(() -> {
ListenableFuture<DaprProtos.InvokeBindingResponse> futureResponse = client.invokeBinding(envelope);
return objectSerializer.deserialize(futureResponse.get().getData().toByteArray(), clazz);
return objectSerializer.deserialize(futureResponse.get().getData().toByteArray(), type);
});
} catch (Exception ex) {
return Mono.error(ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeBinding(
String name, String operation, Object data, Map<String, String> metadata, Class<T> clazz) {
return this.invokeBinding(name, operation, data, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, State<T> state, TypeRef<T> type) {
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), type);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, State<T> state, Class<T> clazz) {
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), clazz);
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, String key, TypeRef<T> type) {
return this.getState(stateStoreName, key, null, null, type);
}
/**
@ -276,7 +341,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, String key, Class<T> clazz) {
return this.getState(stateStoreName, key, null, null, clazz);
return this.getState(stateStoreName, key, null, null, TypeRef.get(clazz));
}
/**
@ -284,7 +349,7 @@ public class DaprClientGrpc implements DaprClient {
*/
@Override
public <T> Mono<State<T>> getState(
String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz) {
String stateStoreName, String key, String etag, StateOptions options, TypeRef<T> type) {
try {
if ((stateStoreName == null) || (stateStoreName.trim().isEmpty())) {
throw new IllegalArgumentException("State store name cannot be null or empty.");
@ -308,21 +373,30 @@ public class DaprClientGrpc implements DaprClient {
} catch (NullPointerException npe) {
return null;
}
return buildStateKeyValue(response, key, options, clazz);
return buildStateKeyValue(response, key, options, type);
});
} catch (Exception ex) {
return Mono.error(ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(
String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz) {
return this.getState(stateStoreName, key, etag, options, TypeRef.get(clazz));
}
private <T> State<T> buildStateKeyValue(
DaprProtos.GetStateResponse response,
String requestedKey,
StateOptions stateOptions,
Class<T> clazz) throws IOException {
TypeRef<T> type) throws IOException {
ByteString payload = response.getData();
byte[] data = payload == null ? null : payload.toByteArray();
T value = stateSerializer.deserialize(data, clazz);
T value = stateSerializer.deserialize(data, type);
String etag = response.getEtag();
String key = requestedKey;
return new State<>(value, key, etag, stateOptions);

View File

@ -11,6 +11,7 @@ import io.dapr.client.domain.Verb;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.Constants;
import io.dapr.utils.TypeRef;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -123,7 +124,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<T> invokeService(
Verb verb, String appId, String method, Object request, Map<String, String> metadata, Class<T> clazz) {
Verb verb, String appId, String method, Object request, Map<String, String> metadata, TypeRef<T> type) {
try {
if (verb == null) {
throw new IllegalArgumentException("Verb cannot be null.");
@ -140,7 +141,7 @@ public class DaprClientHttp implements DaprClient {
Mono<DaprHttp.Response> response = this.client.invokeApi(httMethod, path, metadata, serializedRequestBody, null);
return response.flatMap(r -> {
try {
T object = objectSerializer.deserialize(r.getBody(), clazz);
T object = objectSerializer.deserialize(r.getBody(), type);
if (object == null) {
return Mono.empty();
}
@ -155,13 +156,44 @@ public class DaprClientHttp implements DaprClient {
}
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb,
String appId,
String method,
Object request,
Map<String, String> metadata,
Class<T> clazz) {
return this.invokeService(verb, appId, method, request, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata, TypeRef<T> type) {
return this.invokeService(verb, appId, method, null, metadata, type);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata, Class<T> clazz) {
return this.invokeService(verb, appId, method, null, metadata, clazz);
return this.invokeService(verb, appId, method, null, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, TypeRef<T> type) {
return this.invokeService(verb, appId, method, request, null, type);
}
/**
@ -169,7 +201,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<T> invokeService(Verb verb, String appId, String method, Object request, Class<T> clazz) {
return this.invokeService(verb, appId, method, request, null, clazz);
return this.invokeService(verb, appId, method, request, null, TypeRef.get(clazz));
}
/**
@ -177,7 +209,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public Mono<Void> invokeService(Verb verb, String appId, String method, Object request) {
return this.invokeService(verb, appId, method, request, null, byte[].class).then();
return this.invokeService(verb, appId, method, request, null, TypeRef.BYTE_ARRAY).then();
}
/**
@ -186,7 +218,7 @@ public class DaprClientHttp implements DaprClient {
@Override
public Mono<Void> invokeService(
Verb verb, String appId, String method, Object request, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, request, metadata, byte[].class).then();
return this.invokeService(verb, appId, method, request, metadata, TypeRef.BYTE_ARRAY).then();
}
/**
@ -195,7 +227,7 @@ public class DaprClientHttp implements DaprClient {
@Override
public Mono<Void> invokeService(
Verb verb, String appId, String method, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, null, metadata, byte[].class).then();
return this.invokeService(verb, appId, method, null, metadata, TypeRef.BYTE_ARRAY).then();
}
/**
@ -204,7 +236,7 @@ public class DaprClientHttp implements DaprClient {
@Override
public Mono<byte[]> invokeService(
Verb verb, String appId, String method, byte[] request, Map<String, String> metadata) {
return this.invokeService(verb, appId, method, request, metadata, byte[].class);
return this.invokeService(verb, appId, method, request, metadata, TypeRef.BYTE_ARRAY);
}
/**
@ -212,7 +244,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public Mono<Void> invokeBinding(String name, String operation, Object data) {
return this.invokeBinding(name, operation, data, null, byte[].class).then();
return this.invokeBinding(name, operation, data, null, TypeRef.BYTE_ARRAY).then();
}
/**
@ -220,7 +252,15 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public Mono<byte[]> invokeBinding(String name, String operation, byte[] data, Map<String, String> metadata) {
return this.invokeBinding(name, operation, data, metadata, byte[].class);
return this.invokeBinding(name, operation, data, metadata, TypeRef.BYTE_ARRAY);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeBinding(String name, String operation, Object data, TypeRef<T> type) {
return this.invokeBinding(name, operation, data, null, type);
}
/**
@ -228,7 +268,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<T> invokeBinding(String name, String operation, Object data, Class<T> clazz) {
return this.invokeBinding(name, operation, data, null, clazz);
return this.invokeBinding(name, operation, data, null, TypeRef.get(clazz));
}
/**
@ -236,7 +276,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<T> invokeBinding(
String name, String operation, Object data, Map<String, String> metadata, Class<T> clazz) {
String name, String operation, Object data, Map<String, String> metadata, TypeRef<T> type) {
try {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Binding name cannot be null or empty.");
@ -279,7 +319,7 @@ public class DaprClientHttp implements DaprClient {
Mono<DaprHttp.Response> response = this.client.invokeApi(httpMethod, url.toString(), null, payload, null);
return response.flatMap(r -> {
try {
T object = objectSerializer.deserialize(r.getBody(), clazz);
T object = objectSerializer.deserialize(r.getBody(), type);
if (object == null) {
return Mono.empty();
}
@ -294,12 +334,38 @@ public class DaprClientHttp implements DaprClient {
}
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<T> invokeBinding(
String name, String operation, Object data, Map<String, String> metadata, Class<T> clazz) {
return this.invokeBinding(name, operation, data, metadata, TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, State<T> state, TypeRef<T> type) {
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), type);
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, State<T> state, Class<T> clazz) {
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), clazz);
return this.getState(stateStoreName, state.getKey(), state.getEtag(), state.getOptions(), TypeRef.get(clazz));
}
/**
* {@inheritDoc}
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, String key, TypeRef<T> type) {
return this.getState(stateStoreName, key, null, null, type);
}
/**
@ -307,7 +373,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<State<T>> getState(String stateStoreName, String key, Class<T> clazz) {
return this.getState(stateStoreName, key, null, null, clazz);
return this.getState(stateStoreName, key, null, null, TypeRef.get(clazz));
}
/**
@ -315,7 +381,7 @@ public class DaprClientHttp implements DaprClient {
*/
@Override
public <T> Mono<State<T>> getState(
String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz) {
String stateStoreName, String key, String etag, StateOptions options, TypeRef<T> type) {
try {
if ((stateStoreName == null) || (stateStoreName.trim().isEmpty())) {
throw new IllegalArgumentException("State store name cannot be null or empty.");
@ -341,7 +407,7 @@ public class DaprClientHttp implements DaprClient {
.invokeApi(DaprHttp.HttpMethods.GET.name(), url.toString(), urlParameters, headers)
.flatMap(s -> {
try {
return Mono.just(buildStateKeyValue(s, key, options, clazz));
return Mono.just(buildStateKeyValue(s, key, options, type));
} catch (Exception ex) {
return Mono.error(ex);
}
@ -351,6 +417,12 @@ public class DaprClientHttp implements DaprClient {
}
}
@Override
public <T> Mono<State<T>> getState(
String stateStoreName, String key, String etag, StateOptions options, Class<T> clazz) {
return null;
}
/**
* {@inheritDoc}
*/
@ -453,15 +525,15 @@ public class DaprClientHttp implements DaprClient {
*
* @param response The response of the HTTP Call
* @param requestedKey The Key Requested.
* @param clazz The Class of the Value of the state
* @param type The Class of the Value of the state
* @param <T> The Type of the Value of the state
* @return A StateKeyValue instance
* @throws IOException If there's a issue deserialzing the response.
*/
private <T> State<T> buildStateKeyValue(
DaprHttp.Response response, String requestedKey, StateOptions stateOptions, Class<T> clazz) throws IOException {
DaprHttp.Response response, String requestedKey, StateOptions stateOptions, TypeRef<T> type) throws IOException {
// The state is in the body directly, so we use the state serializer here.
T value = stateSerializer.deserialize(response.getBody(), clazz);
T value = stateSerializer.deserialize(response.getBody(), type);
String key = requestedKey;
String etag = null;
if (response.getHeaders() != null && response.getHeaders().containsKey("Etag")) {

View File

@ -7,10 +7,13 @@ package io.dapr.client;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.client.domain.CloudEvent;
import io.dapr.utils.TypeRef;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* Serializes and deserializes an internal object.
@ -55,6 +58,19 @@ public class ObjectSerializer {
return OBJECT_MAPPER.writeValueAsBytes(state);
}
/**
* Deserializes the byte array into the original object.
*
* @param content Content to be parsed.
* @param type Type of the object being deserialized.
* @param <T> Generic type of the object being deserialized.
* @return Object of type T.
* @throws IOException In case content cannot be deserialized.
*/
public <T> T deserialize(byte[] content, TypeRef<T> type) throws IOException {
return deserialize(content, OBJECT_MAPPER.constructType(type.getType()));
}
/**
* Deserializes the byte array into the original object.
*
@ -65,12 +81,16 @@ public class ObjectSerializer {
* @throws IOException In case content cannot be deserialized.
*/
public <T> T deserialize(byte[] content, Class<T> clazz) throws IOException {
if ((clazz == null) || (clazz == Void.class)) {
return deserialize(content, OBJECT_MAPPER.constructType(clazz));
}
private <T> T deserialize(byte[] content, JavaType javaType) throws IOException {
if ((javaType == null) || javaType.isTypeOrSubTypeOf(Void.class)) {
return null;
}
if (clazz.isPrimitive()) {
return deserializePrimitives(content, clazz);
if (javaType.isPrimitive()) {
return deserializePrimitives(content, javaType);
}
if (content == null) {
@ -78,7 +98,7 @@ public class ObjectSerializer {
}
// Deserialization of GRPC response fails without this check since it does not come as base64 encoded byte[].
if (clazz == byte[].class) {
if (javaType.hasRawClass(byte[].class)) {
return (T) content;
}
@ -86,59 +106,59 @@ public class ObjectSerializer {
return (T) null;
}
if (clazz == CloudEvent.class) {
if (javaType.hasRawClass(CloudEvent.class)) {
return (T) CloudEvent.deserialize(content);
}
return OBJECT_MAPPER.readValue(content, clazz);
return OBJECT_MAPPER.readValue(content, javaType);
}
/**
* 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 <T> Result type.
* @param content Value to be parsed.
* @param javaType Type of the expected result type.
* @param <T> Result type.
* @return Result as corresponding type.
* @throws Exception if cannot deserialize primitive time.
*/
private static <T> T deserializePrimitives(byte[] content, Class<T> clazz) throws IOException {
private static <T> T deserializePrimitives(byte[] content, JavaType javaType) throws IOException {
if ((content == null) || (content.length == 0)) {
if (boolean.class == clazz) {
if (javaType.hasRawClass(boolean.class)) {
return (T) Boolean.FALSE;
}
if (byte.class == clazz) {
if (javaType.hasRawClass(byte.class)) {
return (T) Byte.valueOf((byte) 0);
}
if (short.class == clazz) {
if (javaType.hasRawClass(short.class)) {
return (T) Short.valueOf((short) 0);
}
if (int.class == clazz) {
if (javaType.hasRawClass(int.class)) {
return (T) Integer.valueOf(0);
}
if (long.class == clazz) {
if (javaType.hasRawClass(long.class)) {
return (T) Long.valueOf(0L);
}
if (float.class == clazz) {
if (javaType.hasRawClass(float.class)) {
return (T) Float.valueOf(0);
}
if (double.class == clazz) {
if (javaType.hasRawClass(double.class)) {
return (T) Double.valueOf(0);
}
if (char.class == clazz) {
if (javaType.hasRawClass(char.class)) {
return (T) Character.valueOf(Character.MIN_VALUE);
}
return null;
}
return OBJECT_MAPPER.readValue(content, clazz);
return OBJECT_MAPPER.readValue(content, javaType);
}
}

View File

@ -5,6 +5,8 @@
package io.dapr.serializer;
import io.dapr.utils.TypeRef;
import java.io.IOException;
/**
@ -13,7 +15,7 @@ import java.io.IOException;
public interface DaprObjectSerializer {
/**
* Serializes the given object as a String to be saved.
* Serializes the given object as byte[].
*
* @param o Object to be serialized.
* @return Serialized object.
@ -22,13 +24,13 @@ public interface DaprObjectSerializer {
byte[] serialize(Object o) throws IOException;
/**
* Deserializes the given String into a object.
* Deserializes the given byte[] into a object.
*
* @param data Data to be deserialized.
* @param clazz Class of object to be deserialized.
* @param type Type of object to be deserialized.
* @param <T> Type of object to be deserialized.
* @return Deserialized object.
* @throws IOException If cannot deserialize object.
*/
<T> T deserialize(byte[] data, Class<T> clazz) throws IOException;
<T> T deserialize(byte[] data, TypeRef<T> type) throws IOException;
}

View File

@ -6,8 +6,10 @@
package io.dapr.serializer;
import io.dapr.client.ObjectSerializer;
import io.dapr.utils.TypeRef;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* Default serializer/deserializer for request/response objects and for state objects too.
@ -26,7 +28,7 @@ public class DefaultObjectSerializer extends ObjectSerializer implements DaprObj
* {@inheritDoc}
*/
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) throws IOException {
return super.deserialize(data, clazz);
public <T> T deserialize(byte[] data, TypeRef<T> type) throws IOException {
return super.deserialize(data, type);
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.utils;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Used to reference a type.
*
* <p>Usage: new TypeRef&lt;MyClass&gt;(){}</p>
* @param <T> Type to be deserialized.
*/
public abstract class TypeRef<T> {
public static final TypeRef<String> STRING = new TypeRef<String>() {};
public static final TypeRef<Boolean> BOOLEAN = new TypeRef(boolean.class) {};
public static final TypeRef<Integer> INT = new TypeRef(int.class) {};
public static final TypeRef<Long> LONG = new TypeRef(long.class) {};
public static final TypeRef<Character> CHAR = new TypeRef(char.class) {};
public static final TypeRef<Byte> BYTE = new TypeRef(byte.class) {};
public static final TypeRef<Void> VOID = new TypeRef(void.class) {};
public static final TypeRef<Float> FLOAT = new TypeRef(float.class) {};
public static final TypeRef<Double> DOUBLE = new TypeRef(double.class) {};
public static final TypeRef<byte[]> BYTE_ARRAY = new TypeRef<byte[]>() {};
public static final TypeRef<int[]> INT_ARRAY = new TypeRef<int[]>() {};
public static final TypeRef<String[]> STRING_ARRAY = new TypeRef<String[]>() {};
private final Type type;
/**
* Constructor.
*/
public TypeRef() {
Type superClass = this.getClass().getGenericSuperclass();
if (superClass instanceof Class) {
throw new IllegalArgumentException("TypeReference requires type.");
}
this.type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
}
/**
* Constructor for reflection.
*
* @param clazz Class type to be referenced.
*/
private TypeRef(Class<T> clazz) {
this.type = clazz;
}
/**
* Gets the type referenced.
*
* @return type referenced.
*/
public Type getType() {
return this.type;
}
/**
* Creates a reference to a given class type.
* @param clazz Class type to be referenced.
* @param <T> Type to be referenced.
* @return Class type reference.
*/
public static <T> TypeRef<T> get(Class<T> clazz) {
if (clazz == String.class) {
return (TypeRef<T>) STRING;
}
if (clazz == boolean.class) {
return (TypeRef<T>) BOOLEAN;
}
if (clazz == int.class) {
return (TypeRef<T>) INT;
}
if (clazz == long.class) {
return (TypeRef<T>) LONG;
}
if (clazz == char.class) {
return (TypeRef<T>) CHAR;
}
if (clazz == byte.class) {
return (TypeRef<T>) BYTE;
}
if (clazz == void.class) {
return (TypeRef<T>) VOID;
}
if (clazz == float.class) {
return (TypeRef<T>) FLOAT;
}
if (clazz == double.class) {
return (TypeRef<T>) DOUBLE;
}
if (clazz == byte[].class) {
return (TypeRef<T>) BYTE_ARRAY;
}
if (clazz == int[].class) {
return (TypeRef<T>) INT_ARRAY;
}
if (clazz == String[].class) {
return (TypeRef<T>) STRING_ARRAY;
}
return new TypeRef<T>(clazz) {};
}
}

View File

@ -99,7 +99,7 @@ public class DaprClientHttpTest {
String event = "{ \"message\": \"This is a test\" }";
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Void> mono = daprClientHttp.invokeService(null, "", "", null, null, null);
Mono<Void> mono = daprClientHttp.invokeService(null, "", "", null, null, (Class)null);
assertNull(mono.block());
}
@ -112,19 +112,19 @@ public class DaprClientHttpTest {
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
assertThrows(IllegalArgumentException.class, () -> {
daprClientHttp.invokeService(null, "", "", null, null, null).block();
daprClientHttp.invokeService(null, "", "", null, null, (Class)null).block();
});
assertThrows(IllegalArgumentException.class, () -> {
daprClientHttp.invokeService(Verb.POST, null, "", null, null, null).block();
daprClientHttp.invokeService(Verb.POST, null, "", null, null, (Class)null).block();
});
assertThrows(IllegalArgumentException.class, () -> {
daprClientHttp.invokeService(Verb.POST, "", "", null, null, null).block();
daprClientHttp.invokeService(Verb.POST, "", "", null, null, (Class)null).block();
});
assertThrows(IllegalArgumentException.class, () -> {
daprClientHttp.invokeService(Verb.POST, "1", null, null, null, null).block();
daprClientHttp.invokeService(Verb.POST, "1", null, null, null, (Class)null).block();
});
assertThrows(IllegalArgumentException.class, () -> {
daprClientHttp.invokeService(Verb.POST, "1", "", null, null, null).block();
daprClientHttp.invokeService(Verb.POST, "1", "", null, null, (Class)null).block();
});
}
@ -137,7 +137,7 @@ public class DaprClientHttpTest {
String event = "{ \"message\": \"This is a test\" }";
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Void> mono = daprClientHttp.invokeService(Verb.POST, "1", "", null, null, null);
Mono<Void> mono = daprClientHttp.invokeService(Verb.POST, "1", "", null, null, (Class)null);
assertNull(mono.block());
}
@ -237,6 +237,78 @@ public class DaprClientHttpTest {
assertEquals("OK", mono.block());
}
@Test
public void invokeBindingResponseDouble() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("1.5");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Double> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, double.class);
assertEquals(1.5, mono.block(), 0.0001);
}
@Test
public void invokeBindingResponseFloat() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("1.5");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Float> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, float.class);
assertEquals(1.5, mono.block(), 0.0001);
}
@Test
public void invokeBindingResponseChar() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("\"a\"");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Character> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, char.class);
assertEquals('a', (char)mono.block());
}
@Test
public void invokeBindingResponseByte() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("\"2\"");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Byte> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, byte.class);
assertEquals((byte)0x2, (byte)mono.block());
}
@Test
public void invokeBindingResponseLong() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("1");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Long> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, long.class);
assertEquals(1, (long)mono.block());
}
@Test
public void invokeBindingResponseInt() {
Map<String, String> map = new HashMap<>();
mockInterceptor.addRule()
.post("http://127.0.0.1:3000/v1.0/bindings/sample-topic")
.respond("1");
daprHttp = new DaprHttp(3000, okHttpClient);
daprClientHttp = new DaprClientHttp(daprHttp);
Mono<Integer> mono = daprClientHttp.invokeBinding("sample-topic", "myoperation", "", null, int.class);
assertEquals(1, (int)mono.block());
}
@Test(expected = IllegalArgumentException.class)
public void invokeBindingNullName() {
Map<String, String> map = new HashMap<>();

View File

@ -5,15 +5,22 @@
package io.dapr.serializer;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.client.domain.CloudEvent;
import io.dapr.utils.TypeRef;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.Function;
import static org.junit.Assert.*;
@ -389,15 +396,38 @@ public class DefaultObjectSerializerTest {
expectedResult.setFloatValue(1.0f);
expectedResult.setDoubleValue(1000.0);
MyObjectTestToSerialize result;
try {
result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), MyObjectTestToSerialize.class);
result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), TypeRef.get(MyObjectTestToSerialize.class));
assertEquals("The expected value is different than the actual result", expectedResult, result);
} catch (IOException exception) {
fail(exception.getMessage());
}
}
@Test
public void deserializeArrayObjectTest() {
String jsonToDeserialize = "[{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}]";
MyObjectTestToSerialize expectedResult = new MyObjectTestToSerialize();
expectedResult.setStringValue("A String");
expectedResult.setIntValue(2147483647);
expectedResult.setBoolValue(true);
expectedResult.setCharValue('a');
expectedResult.setByteValue((byte) 65);
expectedResult.setShortValue((short) 32767);
expectedResult.setLongValue(9223372036854775807L);
expectedResult.setFloatValue(1.0f);
expectedResult.setDoubleValue(1000.0);
List<MyObjectTestToSerialize> result;
try {
result = SERIALIZER.deserialize(jsonToDeserialize.getBytes(), new TypeRef<List<MyObjectTestToSerialize>>(){});
assertEquals("The expected value is different than the actual result", expectedResult, result.get(0));
} catch (IOException exception) {
fail(exception.getMessage());
}
}
@Test
public void deserializeBytesTest() {
@ -414,12 +444,10 @@ public class DefaultObjectSerializerTest {
public void deserializeNullObjectOrPrimitiveTest() {
try {
MyObjectTestToSerialize expectedObj = null;
MyObjectTestToSerialize objResult = SERIALIZER.deserialize(null, MyObjectTestToSerialize.class);
assertEquals(expectedObj, objResult);
boolean expectedBoolResutl = false;
assertNull(objResult);
boolean boolResult = SERIALIZER.deserialize(null, boolean.class);
assertEquals(expectedBoolResutl, boolResult);
assertEquals(false, boolResult);
byte expectedByteResult = Byte.valueOf((byte) 0);
byte byteResult = SERIALIZER.deserialize(null, byte.class);
assertEquals(expectedByteResult, byteResult);
@ -785,6 +813,17 @@ public class DefaultObjectSerializerTest {
deserializeData.apply(new ObjectMapper().writeValueAsString("{\"id\": \"123:\", \"name\": \"Jon Doe\"}")));
}
@Test
public void deserializeListOfString() throws IOException {
List<String> r = SERIALIZER.deserialize("[\"1\", \"2\", \"3\"]".getBytes(), new ArrayList<String>().getClass());
assertNotNull(r);
assertEquals(3, r.size());
assertEquals("1", r.get(0));
assertEquals("2", r.get(1));
assertEquals("3", r.get(2));
}
private static String quote(String content) {
if (content == null) {
return null;