Refactor ObjectSerializer class to add ability set custom object mapper. Refactor DaprStateAsyncprovider class to use custom serializers based on ObjectSerializer class as Json serializer. Add unit tests.

This commit is contained in:
Andrey Bogdanov 2021-03-09 16:10:23 +03:00
parent 9296a83323
commit c58b9b3bca
4 changed files with 85 additions and 17 deletions

View File

@ -174,7 +174,7 @@ public class ActorObjectSerializer extends ObjectSerializer {
return null;
}
JsonNode node = OBJECT_MAPPER.readTree(value);
JsonNode node = getObjectMapper().readTree(value);
String callback = node.get("callback").asText();
Duration dueTime = extractDurationOrNull(node, "dueTime");
Duration period = extractDurationOrNull(node, "period");
@ -195,7 +195,7 @@ public class ActorObjectSerializer extends ObjectSerializer {
return null;
}
JsonNode node = OBJECT_MAPPER.readTree(value);
JsonNode node = getObjectMapper().readTree(value);
Duration dueTime = extractDurationOrNull(node, "dueTime");
Duration period = extractDurationOrNull(node, "period");
byte[] data = node.get("data") != null ? node.get("data").binaryValue() : null;

View File

@ -7,6 +7,7 @@ package io.dapr.actors.runtime;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.actors.ActorId;
import io.dapr.client.ObjectSerializer;
import io.dapr.config.Properties;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
@ -56,7 +57,7 @@ class DaprStateAsyncProvider {
DaprStateAsyncProvider(DaprClient daprClient, DaprObjectSerializer stateSerializer) {
this.daprClient = daprClient;
this.stateSerializer = stateSerializer;
this.isStateSerializerDefault = stateSerializer.getClass() == DefaultObjectSerializer.class;
this.isStateSerializerDefault = ObjectSerializer.class.isAssignableFrom(stateSerializer.getClass());
}
<T> Mono<T> load(String actorType, ActorId actorId, String stateName, TypeRef<T> type) {

View File

@ -7,6 +7,7 @@ package io.dapr.actors.runtime;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.actors.ActorId;
import io.dapr.client.ObjectSerializer;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.utils.TypeRef;
@ -31,6 +32,17 @@ public class DaprStateAsyncProviderTest {
private static final double EPSILON = 1e-10;
class CustomJsonSerializer extends ObjectSerializer implements DaprObjectSerializer{
CustomJsonSerializer() {
super(DaprStateAsyncProviderTest.OBJECT_MAPPER);
}
@Override
public String getContentType() {
return "application/json";
}
}
/**
* Class used to test JSON serialization.
*/
@ -124,7 +136,7 @@ public class DaprStateAsyncProviderTest {
})))
.thenReturn(Mono.empty());
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, SERIALIZER);
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, new CustomJsonSerializer());
provider.apply("MyActor",
new ActorId("123"),
createInsertChange("name", "Jon Doe"),
@ -136,6 +148,49 @@ public class DaprStateAsyncProviderTest {
verify(daprClient).saveStateTransactionally(eq("MyActor"), eq("123"), any());
}
@Test
public void happyCaseApplyWithCustomJsonSerializer() {
DaprClient daprClient = mock(DaprClient.class);
when(daprClient
.saveStateTransactionally(
eq("MyActor"),
eq("123"),
argThat(operations -> {
if (operations == null) {
return false;
}
if (operations.size() != 1) {
return false;
}
ActorStateOperation operation = operations.get(0);
if (operation.getOperationType() == null) {
return false;
}
if (operation.getKey() == null) {
return false;
}
String opName = operation.getOperationType();
String key = operation.getKey();
Object value = operation.getValue();
return "upsert".equals(opName) &&
"object".equals(key) &&
"{\"id\":1000,\"name\":\"Roxane\"}".equals(value);
})))
.thenReturn(Mono.empty());
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprClient, SERIALIZER);
provider.apply("MyActor",
new ActorId("123"),
createInsertChange("object", new Customer().setId(1000).setName("Roxane")))
.block();
verify(daprClient).saveStateTransactionally(eq("MyActor"), eq("123"), any());
}
@Test
public void happyCaseLoad() throws Exception {
DaprClient daprClient = mock(DaprClient.class);

View File

@ -22,17 +22,22 @@ import java.lang.reflect.Method;
*/
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);
private final ObjectMapper objectMapper;
/**
* Default constructor to avoid class from being instantiated outside package but still inherited.
*/
protected ObjectSerializer() {
objectMapper = 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(ObjectMapper mapper) {
objectMapper = mapper;
}
/**
@ -62,7 +67,7 @@ public class ObjectSerializer {
}
// Not string, not primitive, so it is a complex type: we use JSON for that.
return OBJECT_MAPPER.writeValueAsBytes(state);
return getObjectMapper().writeValueAsBytes(state);
}
/**
@ -75,7 +80,7 @@ public class ObjectSerializer {
* @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()));
return deserialize(content, getObjectMapper().constructType(type.getType()));
}
/**
@ -88,7 +93,7 @@ public class ObjectSerializer {
* @throws IOException In case content cannot be deserialized.
*/
public <T> T deserialize(byte[] content, Class<T> clazz) throws IOException {
return deserialize(content, OBJECT_MAPPER.constructType(clazz));
return deserialize(content, getObjectMapper().constructType(clazz));
}
private <T> T deserialize(byte[] content, JavaType javaType) throws IOException {
@ -130,7 +135,7 @@ public class ObjectSerializer {
}
}
return OBJECT_MAPPER.readValue(content, javaType);
return getObjectMapper().readValue(content, javaType);
}
/**
@ -141,7 +146,7 @@ public class ObjectSerializer {
* @throws IOException In case content cannot be parsed.
*/
public JsonNode parseNode(byte[] content) throws IOException {
return OBJECT_MAPPER.readTree(content);
return getObjectMapper().readTree(content);
}
/**
@ -153,7 +158,7 @@ public class ObjectSerializer {
* @return Result as corresponding type.
* @throws IOException if cannot deserialize primitive time.
*/
private static <T> T deserializePrimitives(byte[] content, JavaType javaType) throws IOException {
private <T> T deserializePrimitives(byte[] content, JavaType javaType) throws IOException {
if ((content == null) || (content.length == 0)) {
if (javaType.hasRawClass(boolean.class)) {
return (T) Boolean.FALSE;
@ -190,6 +195,13 @@ public class ObjectSerializer {
return null;
}
return OBJECT_MAPPER.readValue(content, javaType);
return getObjectMapper().readValue(content, javaType);
}
/**
* Shared Json serializer/deserializer as per Jackson's documentation.
*/
protected ObjectMapper getObjectMapper() {
return objectMapper;
}
}