Move PojoCloudEventData in core (#289)

* Add a static method to wrap a pojo in PojoCloudEventData

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* WIP

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Cleanup

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Reverted BytesCloudEventData and moved to a separate PR

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
Francesco Guardiani 2020-11-23 15:59:33 +01:00 committed by GitHub
parent 394347db07
commit c9b3fa4b65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 59 deletions

View File

@ -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<T> 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 <T> the source type of the conversion
*/
@FunctionalInterface
public interface ToBytes<T> {
byte[] convert(T data) throws Exception;
}
private final T value;
private byte[] memoizedValue;
private final ToBytes<T> mapper;
private PojoCloudEventData(T value, ToBytes<T> mapper) {
this(value, null, mapper);
}
private PojoCloudEventData(T value, byte[] memoizedValue, ToBytes<T> 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 <T> PojoCloudEventData<T> wrap(T data, ToBytes<T> mapper) {
return new PojoCloudEventData<>(data, mapper);
}
}

View File

@ -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<Integer> 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<Integer> data = PojoCloudEventData.wrap(10, v -> serialized);
assertThat(data.getValue())
.isEqualTo(10);
assertThat(data.toBytes())
.isSameAs(serialized);
}
}

View File

@ -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<T> 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());
}
}

View File

@ -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<T> implements CloudEventDataMapper<PojoCloudEventData<T>> {
@ -30,7 +32,7 @@ public class PojoCloudEventDataMapper<T> implements CloudEventDataMapper<PojoClo
} catch (Exception e) {
throw CloudEventRWException.newDataConversion(e, JsonNode.class.toString(), target.getTypeName());
}
return new PojoCloudEventData<>(mapper, value);
return PojoCloudEventData.wrap(value, mapper::writeValueAsBytes);
}
// Worst case, deserialize from bytes
@ -41,11 +43,12 @@ public class PojoCloudEventDataMapper<T> implements CloudEventDataMapper<PojoClo
} catch (Exception e) {
throw CloudEventRWException.newDataConversion(e, byte[].class.toString(), target.getTypeName());
}
return new PojoCloudEventData<>(mapper, value, bytes);
return PojoCloudEventData.wrap(value, v -> bytes);
}
/**
* Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}&lt;T&gt;.
* Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}&lt;T&gt;
* using a Jackson {@link ObjectMapper}.
*
* <p>
* When working with generic types (e.g. {@link List}&lt;{@link String}&gt;),
@ -62,7 +65,8 @@ public class PojoCloudEventDataMapper<T> implements CloudEventDataMapper<PojoClo
}
/**
* Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}&lt;T&gt;.
* Creates a {@link PojoCloudEventDataMapper} mapping {@link CloudEventData} into {@link PojoCloudEventData}&lt;T&gt;
* using a Jackson {@link ObjectMapper}.
*
* <p>
* This overload is more suitable for mapping generic objects (e.g. {@link List}&lt;{@link String}&gt;),

View File

@ -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;