From c9b3fa4b65c8295f3a4ff5ed2d6ccf24f8312c9b Mon Sep 17 00:00:00 2001 From: Francesco Guardiani Date: Mon, 23 Nov 2020 15:59:33 +0100 Subject: [PATCH] Move PojoCloudEventData in core (#289) * Add a static method to wrap a pojo in PojoCloudEventData Signed-off-by: Francesco Guardiani * WIP Signed-off-by: Francesco Guardiani * Cleanup Signed-off-by: Francesco Guardiani * Reverted BytesCloudEventData and moved to a separate PR Signed-off-by: Francesco Guardiani --- .../core/data/PojoCloudEventData.java | 74 +++++++++++++++++++ .../core/data/PojoCloudEventDataTest.java | 36 +++++++++ .../jackson/PojoCloudEventData.java | 54 -------------- .../jackson/PojoCloudEventDataMapper.java | 12 ++- .../jackson/PojoCloudEventDataMapperTest.java | 2 +- 5 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 core/src/main/java/io/cloudevents/core/data/PojoCloudEventData.java create mode 100644 core/src/test/java/io/cloudevents/core/data/PojoCloudEventDataTest.java delete mode 100644 formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventData.java diff --git a/core/src/main/java/io/cloudevents/core/data/PojoCloudEventData.java b/core/src/main/java/io/cloudevents/core/data/PojoCloudEventData.java new file mode 100644 index 00000000..87fcb690 --- /dev/null +++ b/core/src/main/java/io/cloudevents/core/data/PojoCloudEventData.java @@ -0,0 +1,74 @@ +package io.cloudevents.core.data; + +import io.cloudevents.CloudEventData; +import io.cloudevents.rw.CloudEventRWException; + +import java.util.Objects; + +public class PojoCloudEventData implements CloudEventData { + + /** + * Interface defining a conversion from T to byte array. This is similar to {@link java.util.function.Function} + * but it allows checked exceptions. + * + * @param the source type of the conversion + */ + @FunctionalInterface + public interface ToBytes { + byte[] convert(T data) throws Exception; + } + + private final T value; + private byte[] memoizedValue; + private final ToBytes mapper; + + private PojoCloudEventData(T value, ToBytes mapper) { + this(value, null, mapper); + } + + private PojoCloudEventData(T value, byte[] memoizedValue, ToBytes mapper) { + Objects.requireNonNull(value); + if (memoizedValue == null && mapper == null) { + throw new NullPointerException("You must provide the serialized data value or a mapper"); + } + this.value = value; + this.memoizedValue = memoizedValue; + this.mapper = mapper; + } + + public T getValue() { + return value; + } + + @Override + public byte[] toBytes() { + if (this.memoizedValue == null) { + try { + this.memoizedValue = mapper.convert(this.value); + } catch (Exception e) { + throw CloudEventRWException.newDataConversion(e, value.getClass().toString(), "byte[]"); + } + } + return this.memoizedValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PojoCloudEventData that = (PojoCloudEventData) o; + return Objects.equals(getValue(), that.getValue()); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + /** + * Wrap the provided data in a {@link PojoCloudEventData} serializable by the provided mapper. + */ + public static PojoCloudEventData wrap(T data, ToBytes mapper) { + return new PojoCloudEventData<>(data, mapper); + } +} diff --git a/core/src/test/java/io/cloudevents/core/data/PojoCloudEventDataTest.java b/core/src/test/java/io/cloudevents/core/data/PojoCloudEventDataTest.java new file mode 100644 index 00000000..8270009b --- /dev/null +++ b/core/src/test/java/io/cloudevents/core/data/PojoCloudEventDataTest.java @@ -0,0 +1,36 @@ +package io.cloudevents.core.data; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PojoCloudEventDataTest { + + @Test + void testWrapAndMemoization() { + PojoCloudEventData data = PojoCloudEventData.wrap(10, i -> i.toString().getBytes()); + + assertThat(data.getValue()) + .isEqualTo(10); + + byte[] firstConversion = data.toBytes(); + + assertThat(firstConversion) + .isEqualTo("10".getBytes()); + + assertThat(data.toBytes()) + .isSameAs(firstConversion); + } + + @Test + void testAlreadySerializedValue() { + byte[] serialized = "10".getBytes(); + PojoCloudEventData data = PojoCloudEventData.wrap(10, v -> serialized); + + assertThat(data.getValue()) + .isEqualTo(10); + + assertThat(data.toBytes()) + .isSameAs(serialized); + } +} diff --git a/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventData.java b/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventData.java deleted file mode 100644 index 30f11c3f..00000000 --- a/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventData.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.cloudevents.jackson; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.cloudevents.CloudEventData; -import io.cloudevents.rw.CloudEventRWException; - -import java.util.Objects; - -public class PojoCloudEventData implements CloudEventData { - - private final ObjectMapper mapper; - private byte[] memoizedValue; - private final T value; - - protected PojoCloudEventData(ObjectMapper mapper, T value) { - this(mapper, value, null); - } - - protected PojoCloudEventData(ObjectMapper mapper, T value, byte[] memoizedValue) { - this.mapper = mapper; - this.value = value; - this.memoizedValue = memoizedValue; - } - - public T getValue() { - return value; - } - - @Override - public byte[] toBytes() { - if (this.memoizedValue == null) { - try { - this.memoizedValue = mapper.writeValueAsBytes(value); - } catch (JsonProcessingException e) { - throw CloudEventRWException.newDataConversion(e, value.getClass().toString(), "byte[]"); - } - } - return this.memoizedValue; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PojoCloudEventData that = (PojoCloudEventData) o; - return Objects.equals(getValue(), that.getValue()); - } - - @Override - public int hashCode() { - return Objects.hash(getValue()); - } -} diff --git a/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventDataMapper.java b/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventDataMapper.java index 885c9ebd..09ac4f54 100644 --- a/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventDataMapper.java +++ b/formats/json-jackson/src/main/java/io/cloudevents/jackson/PojoCloudEventDataMapper.java @@ -5,8 +5,10 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.cloudevents.CloudEventData; +import io.cloudevents.core.data.PojoCloudEventData; import io.cloudevents.rw.CloudEventDataMapper; import io.cloudevents.rw.CloudEventRWException; + import java.util.List; public class PojoCloudEventDataMapper implements CloudEventDataMapper> { @@ -30,7 +32,7 @@ public class PojoCloudEventDataMapper implements CloudEventDataMapper(mapper, value); + return PojoCloudEventData.wrap(value, mapper::writeValueAsBytes); } // Worst case, deserialize from bytes @@ -41,11 +43,12 @@ public class PojoCloudEventDataMapper implements CloudEventDataMapper(mapper, value, bytes); + return PojoCloudEventData.wrap(value, v -> bytes); } /** - * Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}<T>. + * Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}<T> + * using a Jackson {@link ObjectMapper}. * *

* When working with generic types (e.g. {@link List}<{@link String}>), @@ -62,7 +65,8 @@ public class PojoCloudEventDataMapper implements CloudEventDataMapper * This overload is more suitable for mapping generic objects (e.g. {@link List}<{@link String}>), diff --git a/formats/json-jackson/src/test/java/io/cloudevents/jackson/PojoCloudEventDataMapperTest.java b/formats/json-jackson/src/test/java/io/cloudevents/jackson/PojoCloudEventDataMapperTest.java index cda97801..f93ef1cc 100644 --- a/formats/json-jackson/src/test/java/io/cloudevents/jackson/PojoCloudEventDataMapperTest.java +++ b/formats/json-jackson/src/test/java/io/cloudevents/jackson/PojoCloudEventDataMapperTest.java @@ -7,8 +7,8 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import io.cloudevents.CloudEvent; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.core.data.PojoCloudEventData; import io.cloudevents.core.test.Data; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource;