Progress on implementing serialization
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
7dcfdba30d
commit
4df01cd279
|
@ -16,7 +16,6 @@ public final class EventFormatProvider {
|
|||
|
||||
private HashMap<String, EventFormat> formats;
|
||||
|
||||
//TODO register stuff with SPI
|
||||
private EventFormatProvider() {
|
||||
this.formats = new HashMap<>();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import io.cloudevents.message.*;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
public final class CloudEventImpl implements CloudEvent, BinaryMessage {
|
||||
public final class CloudEventImpl implements CloudEvent, BinaryMessage, BinaryMessageExtensions {
|
||||
|
||||
private final AttributesInternal attributes;
|
||||
private final byte[] data;
|
||||
|
@ -71,10 +71,7 @@ public final class CloudEventImpl implements CloudEvent, BinaryMessage {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends BinaryMessageVisitor<V>, V> V visit(BinaryMessageVisitorFactory<T, V> visitorFactory) throws MessageVisitException, IllegalStateException {
|
||||
BinaryMessageVisitor<V> visitor = visitorFactory.createBinaryMessageVisitor(this.attributes.getSpecVersion());
|
||||
this.attributes.visit(visitor);
|
||||
|
||||
public void visitExtensions(BinaryMessageExtensionsVisitor visitor) throws MessageVisitException {
|
||||
// TODO to be improved
|
||||
for (Map.Entry<String, Object> entry : this.extensions.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
|
@ -88,6 +85,13 @@ public final class CloudEventImpl implements CloudEvent, BinaryMessage {
|
|||
throw new IllegalStateException("Illegal value inside extensions map: " + entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BinaryMessageVisitor<V>, V> V visit(BinaryMessageVisitorFactory<T, V> visitorFactory) throws MessageVisitException, IllegalStateException {
|
||||
BinaryMessageVisitor<V> visitor = visitorFactory.createBinaryMessageVisitor(this.attributes.getSpecVersion());
|
||||
this.attributes.visitAttributes(visitor);
|
||||
this.visitExtensions(visitor);
|
||||
|
||||
if (this.data != null) {
|
||||
visitor.setBody(this.data);
|
||||
|
|
|
@ -3,10 +3,9 @@ package io.cloudevents.message;
|
|||
public interface BinaryMessageAttributes {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param visitor
|
||||
* @throws MessageVisitException
|
||||
*/
|
||||
void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException;
|
||||
void visitAttributes(BinaryMessageAttributesVisitor visitor) throws MessageVisitException;
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.cloudevents.types.Time;
|
|||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BinaryMessageAttributesVisitor {
|
||||
|
||||
void setAttribute(String name, String value) throws MessageVisitException;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.cloudevents.message;
|
||||
|
||||
public interface BinaryMessageExtensions {
|
||||
|
||||
/**
|
||||
* @param visitor
|
||||
* @throws MessageVisitException
|
||||
*/
|
||||
void visitExtensions(BinaryMessageExtensionsVisitor visitor) throws MessageVisitException;
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.cloudevents.message;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BinaryMessageExtensionsVisitor {
|
||||
|
||||
void setExtension(String name, String value) throws MessageVisitException;
|
||||
|
|
|
@ -93,7 +93,7 @@ public final class AttributesImpl implements AttributesInternal {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException {
|
||||
public void visitAttributes(BinaryMessageAttributesVisitor visitor) throws MessageVisitException {
|
||||
visitor.setAttribute(
|
||||
ContextAttributes.ID.name().toLowerCase(),
|
||||
this.id
|
||||
|
|
|
@ -108,7 +108,7 @@ public final class AttributesImpl implements AttributesInternal {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException {
|
||||
public void visitAttributes(BinaryMessageAttributesVisitor visitor) throws MessageVisitException {
|
||||
visitor.setAttribute(
|
||||
ContextAttributes.ID.name().toLowerCase(),
|
||||
this.id
|
||||
|
|
|
@ -1,23 +1,128 @@
|
|||
package io.cloudevents.format.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.impl.AttributesInternal;
|
||||
import io.cloudevents.impl.CloudEventImpl;
|
||||
import io.cloudevents.message.BinaryMessageAttributesVisitor;
|
||||
import io.cloudevents.message.BinaryMessageExtensionsVisitor;
|
||||
import io.cloudevents.message.MessageVisitException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CloudEventSerializer extends StdSerializer<CloudEvent> {
|
||||
protected CloudEventSerializer() {
|
||||
super(CloudEvent.class);
|
||||
}
|
||||
|
||||
private static class AttributesSerializer implements BinaryMessageAttributesVisitor {
|
||||
|
||||
private JsonGenerator gen;
|
||||
|
||||
public AttributesSerializer(JsonGenerator gen) {
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) throws MessageVisitException {
|
||||
try {
|
||||
gen.writeStringField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExtensionsSerializer implements BinaryMessageExtensionsVisitor {
|
||||
|
||||
private JsonGenerator gen;
|
||||
private SerializerProvider provider;
|
||||
|
||||
public ExtensionsSerializer(JsonGenerator gen, SerializerProvider provider) {
|
||||
this.gen = gen;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtension(String name, String value) throws MessageVisitException {
|
||||
try {
|
||||
gen.writeStringField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtension(String name, Number value) throws MessageVisitException {
|
||||
try {
|
||||
gen.writeFieldName(name);
|
||||
provider.findValueSerializer(value.getClass()).serialize(value, gen, provider);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtension(String name, Boolean value) throws MessageVisitException {
|
||||
try {
|
||||
gen.writeBooleanField(name, value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(CloudEvent value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
JsonSerializer<Object> attributesSerializer = provider.findValueSerializer(value.getAttributes().getClass());
|
||||
// Serialize attributes
|
||||
attributesSerializer.serialize(value.getAttributes(), gen, provider);
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("specversion", value.getAttributes().getSpecVersion().toString());
|
||||
|
||||
// Serialize attributes
|
||||
AttributesInternal attributesInternal = (AttributesInternal) value.getAttributes();
|
||||
try {
|
||||
attributesInternal.visitAttributes(new AttributesSerializer(gen));
|
||||
} catch (RuntimeException e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
|
||||
// Serialize extensions
|
||||
try {
|
||||
((CloudEventImpl) value).visitExtensions(new ExtensionsSerializer(gen, provider));
|
||||
} catch (RuntimeException e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
|
||||
// Serialize data
|
||||
Optional<byte[]> dataOptional = value.getData();
|
||||
String contentType = attributesInternal.getDataContentType().orElse(null);
|
||||
if (dataOptional.isPresent()) {
|
||||
if (JsonFormat.shouldSerializeBase64(contentType)) {
|
||||
switch (attributesInternal.getSpecVersion()) {
|
||||
case V03:
|
||||
gen.writeStringField("datacontentencoding", "base64");
|
||||
gen.writeFieldName("data");
|
||||
gen.writeBinary(dataOptional.get());
|
||||
break;
|
||||
case V1:
|
||||
gen.writeFieldName("data_base64");
|
||||
gen.writeBinary(dataOptional.get());
|
||||
break;
|
||||
}
|
||||
} else if (JsonFormat.isJsonContentType(contentType)) {
|
||||
// TODO really bad, is there another solution out there?
|
||||
char[] data = new String(dataOptional.get(), StandardCharsets.UTF_8).toCharArray();
|
||||
gen.writeFieldName("data");
|
||||
gen.writeRawValue(data, 0, data.length);
|
||||
} else {
|
||||
byte[] data = dataOptional.get();
|
||||
gen.writeFieldName("data");
|
||||
gen.writeUTF8String(data, 0, data.length);
|
||||
}
|
||||
}
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,14 @@ import java.util.Set;
|
|||
|
||||
public final class JsonFormat implements EventFormat {
|
||||
|
||||
public static final String CONTENT_TYPE = "application/cloudevents+json";
|
||||
|
||||
public static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
// Add CloudEvent serializer/deserializer
|
||||
MAPPER.registerModule(getCloudEventJacksonModule());
|
||||
|
||||
// add ZonedDateTime ser/de
|
||||
final SimpleModule module = new SimpleModule("Custom ZonedDateTime");
|
||||
module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer());
|
||||
|
@ -41,8 +46,8 @@ public final class JsonFormat implements EventFormat {
|
|||
MAPPER.registerModule(module);
|
||||
}
|
||||
|
||||
private boolean forceDataBase64Serialization = false;
|
||||
private boolean forceStringSerialization = false;
|
||||
private static boolean forceDataBase64Serialization = false;
|
||||
private static boolean forceStringSerialization = false;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(CloudEvent event) throws EventSerializationException {
|
||||
|
@ -64,16 +69,34 @@ public final class JsonFormat implements EventFormat {
|
|||
|
||||
@Override
|
||||
public Set<String> supportedContentTypes() {
|
||||
return Collections.singleton("application/cloudevents+json");
|
||||
return Collections.singleton(CONTENT_TYPE);
|
||||
}
|
||||
|
||||
public JsonFormat forceDataBase64Serialization(boolean forceBase64Serialization) {
|
||||
this.forceDataBase64Serialization = forceBase64Serialization;
|
||||
return this;
|
||||
public static SimpleModule getCloudEventJacksonModule() {
|
||||
final SimpleModule ceModule = new SimpleModule("CloudEvent");
|
||||
ceModule.addSerializer(CloudEvent.class, new CloudEventSerializer());
|
||||
ceModule.addDeserializer(CloudEvent.class, new CloudEventDeserializer());
|
||||
return ceModule;
|
||||
}
|
||||
|
||||
public JsonFormat forceDataStringSerialization(boolean forceStringSerialization) {
|
||||
this.forceStringSerialization = forceStringSerialization;
|
||||
return this;
|
||||
public static void forceDataBase64Serialization(boolean forceBase64Serialization) {
|
||||
JsonFormat.forceDataBase64Serialization = forceBase64Serialization;
|
||||
}
|
||||
|
||||
public static void forceDataStringSerialization(boolean forceStringSerialization) {
|
||||
JsonFormat.forceStringSerialization = forceStringSerialization;
|
||||
}
|
||||
|
||||
protected static boolean isJsonContentType(String contentType) {
|
||||
// If content type, spec states that we should assume is json
|
||||
return contentType == null || contentType.startsWith("application/json") || contentType.startsWith("text/json");
|
||||
}
|
||||
|
||||
protected static boolean shouldSerializeBase64(String contentType) {
|
||||
if (isJsonContentType(contentType)) {
|
||||
return JsonFormat.forceDataBase64Serialization;
|
||||
} else {
|
||||
return !JsonFormat.forceStringSerialization;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ class JsonFormatTest {
|
|||
}
|
||||
|
||||
private EventFormat getFormat() {
|
||||
return EventFormatProvider.getInstance().resolveFormat("application/cloudevents+json");
|
||||
return EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue