diff --git a/core/src/main/java/io/cloudevents/core/builder/CloudEventBuilder.java b/core/src/main/java/io/cloudevents/core/builder/CloudEventBuilder.java index 93522bf9..a1bcfb68 100644 --- a/core/src/main/java/io/cloudevents/core/builder/CloudEventBuilder.java +++ b/core/src/main/java/io/cloudevents/core/builder/CloudEventBuilder.java @@ -23,12 +23,14 @@ import io.cloudevents.SpecVersion; import io.cloudevents.rw.CloudEventWriter; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNullableByDefault; import java.net.URI; import java.time.ZonedDateTime; /** * Builder interface to build a {@link CloudEvent}. */ +@ParametersAreNullableByDefault public interface CloudEventBuilder extends CloudEventWriter { /** @@ -121,7 +123,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param value value of the extension attribute * @return self */ - CloudEventBuilder withExtension(String key, String value); + CloudEventBuilder withExtension(@Nonnull String key, String value); /** * Set an extension with provided key and numeric value @@ -130,7 +132,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param value value of the extension attribute * @return self */ - CloudEventBuilder withExtension(String key, Number value); + CloudEventBuilder withExtension(@Nonnull String key, Number value); /** * Set an extension with provided key and boolean value @@ -139,7 +141,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param value value of the extension attribute * @return self */ - CloudEventBuilder withExtension(String key, boolean value); + CloudEventBuilder withExtension(@Nonnull String key, boolean value); /** * Add to the builder all the extension key/values of the provided extension @@ -147,7 +149,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param extension materialized extension to set in the event * @return self */ - CloudEventBuilder withExtension(Extension extension); + CloudEventBuilder withExtension(@Nonnull Extension extension); /** * Build the event @@ -175,7 +177,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param event event to bootstrap the builder * @return a new CloudEvent v1 builder filled with content of {@code event} */ - static io.cloudevents.core.v1.CloudEventBuilder v1(CloudEvent event) { + static io.cloudevents.core.v1.CloudEventBuilder v1(@Nonnull CloudEvent event) { return new io.cloudevents.core.v1.CloudEventBuilder(event); } @@ -190,7 +192,7 @@ public interface CloudEventBuilder extends CloudEventWriter { * @param event event to bootstrap the builder * @return a new CloudEvent v0.3 builder filled with content of {@code event} */ - static io.cloudevents.core.v03.CloudEventBuilder v03(CloudEvent event) { + static io.cloudevents.core.v03.CloudEventBuilder v03(@Nonnull CloudEvent event) { return new io.cloudevents.core.v03.CloudEventBuilder(event); } diff --git a/core/src/main/java/io/cloudevents/core/extensions/DistributedTracingExtension.java b/core/src/main/java/io/cloudevents/core/extensions/DistributedTracingExtension.java index 5490966f..2165a8f0 100644 --- a/core/src/main/java/io/cloudevents/core/extensions/DistributedTracingExtension.java +++ b/core/src/main/java/io/cloudevents/core/extensions/DistributedTracingExtension.java @@ -26,6 +26,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +/** + * This extension embeds context from Distributed Tracing so that distributed systems can include traces that span an event-driven system. + * + * @see https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md + */ public final class DistributedTracingExtension implements Extension { public static final String TRACEPARENT = "traceparent"; diff --git a/core/src/main/java/io/cloudevents/core/format/EventDeserializationException.java b/core/src/main/java/io/cloudevents/core/format/EventDeserializationException.java index 718489a5..ce51c064 100644 --- a/core/src/main/java/io/cloudevents/core/format/EventDeserializationException.java +++ b/core/src/main/java/io/cloudevents/core/format/EventDeserializationException.java @@ -17,6 +17,9 @@ package io.cloudevents.core.format; +/** + * Exception representing a deserialization error while using an {@link EventFormat}. + */ public class EventDeserializationException extends RuntimeException { public EventDeserializationException(Throwable e) { super(e); diff --git a/core/src/main/java/io/cloudevents/core/format/EventFormat.java b/core/src/main/java/io/cloudevents/core/format/EventFormat.java index 5f5fe180..6acfcae2 100644 --- a/core/src/main/java/io/cloudevents/core/format/EventFormat.java +++ b/core/src/main/java/io/cloudevents/core/format/EventFormat.java @@ -19,19 +19,53 @@ package io.cloudevents.core.format; import io.cloudevents.CloudEvent; +import javax.annotation.ParametersAreNonnullByDefault; import java.util.Collections; import java.util.Set; +/** + * An Event format + * specifies how to serialize a CloudEvent as a sequence of bytes.
+ *

+ * An implementation of this interface should support all specification versions of {@link CloudEvent}.
+ *

+ * Implementations of this interface can be registered to the {@link io.cloudevents.core.provider.EventFormatProvider} to use them. + * + * @see io.cloudevents.core.provider.EventFormatProvider + */ +@ParametersAreNonnullByDefault public interface EventFormat { + /** + * Serialize a {@link CloudEvent} to a byte array. + * + * @param event the event to serialize. + * @return the byte representation of the provided event. + * @throws EventSerializationException if something goes wrong during serialization. + */ byte[] serialize(CloudEvent event) throws EventSerializationException; - CloudEvent deserialize(byte[] event) throws EventDeserializationException; + /** + * Deserialize a byte array to a {@link CloudEvent}. + * + * @param bytes the serialized event. + * @return the deserialized event. + * @throws EventDeserializationException if something goes wrong during deserialization. + */ + CloudEvent deserialize(byte[] bytes) throws EventDeserializationException; + /** + * @return the set of content types this event format can deserialize. These content types are used + * by the {@link io.cloudevents.core.provider.EventFormatProvider} to resolve an {@link EventFormat} starting + * from the content type {@link String}. + */ default Set deserializableContentTypes() { return Collections.singleton(serializedContentType()); } + /** + * @return The content type to use when writing an event with this {@link EventFormat}. + */ String serializedContentType(); } diff --git a/core/src/main/java/io/cloudevents/core/format/EventSerializationException.java b/core/src/main/java/io/cloudevents/core/format/EventSerializationException.java index 76796aef..4fe9d26b 100644 --- a/core/src/main/java/io/cloudevents/core/format/EventSerializationException.java +++ b/core/src/main/java/io/cloudevents/core/format/EventSerializationException.java @@ -17,6 +17,9 @@ package io.cloudevents.core.format; +/** + * Exception representing a serialization error while using an {@link EventFormat}. + */ public class EventSerializationException extends RuntimeException { public EventSerializationException(Throwable e) { super(e); diff --git a/core/src/main/java/io/cloudevents/core/impl/BaseCloudEventBuilder.java b/core/src/main/java/io/cloudevents/core/impl/BaseCloudEventBuilder.java index 22aa617b..12a3e5d7 100644 --- a/core/src/main/java/io/cloudevents/core/impl/BaseCloudEventBuilder.java +++ b/core/src/main/java/io/cloudevents/core/impl/BaseCloudEventBuilder.java @@ -22,6 +22,7 @@ import io.cloudevents.Extension; import io.cloudevents.core.builder.CloudEventBuilder; import io.cloudevents.rw.CloudEventRWException; +import javax.annotation.Nonnull; import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -75,22 +76,22 @@ public abstract class BaseCloudEventBuilderCloudEvent message. */ +@ParametersAreNonnullByDefault public interface MessageReader extends StructuredMessageReader, CloudEventReader { /** diff --git a/core/src/main/java/io/cloudevents/core/message/MessageWriter.java b/core/src/main/java/io/cloudevents/core/message/MessageWriter.java index 22ffb995..7ec534f9 100644 --- a/core/src/main/java/io/cloudevents/core/message/MessageWriter.java +++ b/core/src/main/java/io/cloudevents/core/message/MessageWriter.java @@ -24,11 +24,14 @@ import io.cloudevents.core.message.impl.GenericStructuredMessageReader; import io.cloudevents.rw.CloudEventWriter; import io.cloudevents.rw.CloudEventWriterFactory; +import javax.annotation.ParametersAreNonnullByDefault; + /** * Interface to write the {@link MessageReader} content (CloudEvents attributes, extensions and payload) to a new representation. * * @param return value at the end of the write process. */ +@ParametersAreNonnullByDefault public interface MessageWriter, R> extends CloudEventWriterFactory, StructuredMessageWriter { /** diff --git a/core/src/main/java/io/cloudevents/core/message/StructuredMessageReader.java b/core/src/main/java/io/cloudevents/core/message/StructuredMessageReader.java index 231b217a..b033d5b6 100644 --- a/core/src/main/java/io/cloudevents/core/message/StructuredMessageReader.java +++ b/core/src/main/java/io/cloudevents/core/message/StructuredMessageReader.java @@ -22,10 +22,13 @@ import io.cloudevents.core.format.EventFormat; import io.cloudevents.core.message.impl.GenericStructuredMessageReader; import io.cloudevents.rw.CloudEventRWException; +import javax.annotation.ParametersAreNonnullByDefault; + /** * Represents a CloudEvent message in structured mode. */ @FunctionalInterface +@ParametersAreNonnullByDefault public interface StructuredMessageReader { /** diff --git a/core/src/main/java/io/cloudevents/core/message/StructuredMessageWriter.java b/core/src/main/java/io/cloudevents/core/message/StructuredMessageWriter.java index c132b041..35598ad5 100644 --- a/core/src/main/java/io/cloudevents/core/message/StructuredMessageWriter.java +++ b/core/src/main/java/io/cloudevents/core/message/StructuredMessageWriter.java @@ -20,11 +20,20 @@ package io.cloudevents.core.message; import io.cloudevents.core.format.EventFormat; import io.cloudevents.rw.CloudEventRWException; -//TODO javadoc +import javax.annotation.ParametersAreNonnullByDefault; + +/** + * Interface to write the {@link MessageReader} content (CloudEvents attributes, extensions and payload) to a new representation structured representation. + * + * @param return value at the end of the write process. + */ +@ParametersAreNonnullByDefault @FunctionalInterface public interface StructuredMessageWriter { - // TODO one day we'll convert this to some byte stream + /** + * Write an event using the provided {@link EventFormat}. + */ T setEvent(EventFormat format, byte[] value) throws CloudEventRWException; } diff --git a/core/src/main/java/io/cloudevents/core/message/impl/MessageUtils.java b/core/src/main/java/io/cloudevents/core/message/impl/MessageUtils.java index a78af681..4a8628d0 100644 --- a/core/src/main/java/io/cloudevents/core/message/impl/MessageUtils.java +++ b/core/src/main/java/io/cloudevents/core/message/impl/MessageUtils.java @@ -32,14 +32,7 @@ import java.util.stream.Stream; public class MessageUtils { /** - * Common flow to parse an incoming message that could be structured or binary - * - * @param contentTypeHeaderReader - * @param structuredMessageFactory - * @param specVersionHeaderReader - * @param binaryMessageFactory - * @param unknownMessageFactory - * @return + * Common flow to parse an incoming message that could be structured or binary.
*/ public static MessageReader parseStructuredOrBinaryMessage( Supplier contentTypeHeaderReader, @@ -72,7 +65,7 @@ public class MessageUtils { * * @param headerNameMapping mapper to generate the header name * @param Header key type - * @return + * @return the generated mapping */ public static Map generateAttributesToHeadersMapping(Function headerNameMapping) { return Stream.concat( diff --git a/core/src/main/java/io/cloudevents/core/provider/EventFormatProvider.java b/core/src/main/java/io/cloudevents/core/provider/EventFormatProvider.java index 446100f4..68d70185 100644 --- a/core/src/main/java/io/cloudevents/core/provider/EventFormatProvider.java +++ b/core/src/main/java/io/cloudevents/core/provider/EventFormatProvider.java @@ -18,17 +18,30 @@ package io.cloudevents.core.provider; import io.cloudevents.core.format.EventFormat; +import io.cloudevents.lang.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; import java.util.HashMap; import java.util.ServiceLoader; import java.util.stream.StreamSupport; +/** + * Singleton holding the discovered {@link EventFormat} implementations through {@link ServiceLoader}.
+ *

+ * You can resolve an event format using {@code EventFormatProvider.getInstance().resolveFormat(contentType)}.
+ *

+ * You can programmatically add a new {@link EventFormat} implementation using {@link this#registerFormat(EventFormat)}. + */ +@ParametersAreNonnullByDefault public final class EventFormatProvider { private static class SingletonContainer { private final static EventFormatProvider INSTANCE = new EventFormatProvider(); } + /** + * @return instance of {@link EventFormatProvider} + */ public static EventFormatProvider getInstance() { return EventFormatProvider.SingletonContainer.INSTANCE; } @@ -44,18 +57,30 @@ public final class EventFormatProvider { ).forEach(this::registerFormat); } + /** + * Register a new {@link EventFormat} programmatically. + * + * @param format the new format to register + */ public void registerFormat(EventFormat format) { for (String k : format.deserializableContentTypes()) { this.formats.put(k, format); } } - public EventFormat resolveFormat(String key) { - int i = key.indexOf(';'); + /** + * Resolve an event format starting from the content type. + * + * @param contentType the content type to resolve the event format + * @return null if no format was found for the provided content type + */ + @Nullable + public EventFormat resolveFormat(String contentType) { + int i = contentType.indexOf(';'); if (i != -1) { - key = key.substring(0, i); + contentType = contentType.substring(0, i); } - return this.formats.get(key); + return this.formats.get(contentType); } } diff --git a/core/src/main/java/io/cloudevents/core/provider/ExtensionProvider.java b/core/src/main/java/io/cloudevents/core/provider/ExtensionProvider.java index cde37d33..39181d73 100644 --- a/core/src/main/java/io/cloudevents/core/provider/ExtensionProvider.java +++ b/core/src/main/java/io/cloudevents/core/provider/ExtensionProvider.java @@ -17,14 +17,22 @@ package io.cloudevents.core.provider; -import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventExtensions; import io.cloudevents.Extension; import io.cloudevents.core.extensions.DatarefExtension; import io.cloudevents.core.extensions.DistributedTracingExtension; +import io.cloudevents.lang.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; import java.util.HashMap; import java.util.function.Supplier; +/** + * Singleton to materialize CloudEvent extensions as POJOs.
+ *

+ * You can materialize an {@link Extension} POJO with {@code ExtensionProvider.getInstance().parseExtension(DistributedTracingExtension.class, event)}.
+ */ +@ParametersAreNonnullByDefault public final class ExtensionProvider { private static class SingletonContainer { @@ -35,7 +43,7 @@ public final class ExtensionProvider { return SingletonContainer.INSTANCE; } - private final HashMap, Supplier> extensionFactories; + private final HashMap, Supplier> extensionFactories; // TODO SPI in future? private ExtensionProvider() { @@ -44,16 +52,31 @@ public final class ExtensionProvider { registerExtension(DatarefExtension.class, DatarefExtension::new); } - public void registerExtension(Class extensionClass, Supplier factory) { + /** + * Register a new extension type. + * + * @param extensionClass the class implementing {@link Extension} + * @param factory the empty arguments factory + * @param the type of the extension + */ + public void registerExtension(Class extensionClass, Supplier factory) { this.extensionFactories.put(extensionClass, factory); } + /** + * Parse an extension from the {@link CloudEventExtensions}, materializing the corresponding POJO.
+ * + * @param extensionClass the class implementing {@link Extension} + * @param eventExtensions the event extensions to read + * @param the type of the extension + */ @SuppressWarnings("unchecked") - public T parseExtension(Class extensionClass, CloudEvent event) { - Supplier factory = extensionFactories.get(extensionClass); + @Nullable + public T parseExtension(Class extensionClass, CloudEventExtensions eventExtensions) { + Supplier factory = extensionFactories.get(extensionClass); if (factory != null) { - Extension ext = factory.get(); - ext.readFrom(event); + Extension ext = (Extension) factory.get(); + ext.readFrom(eventExtensions); return (T) ext; } return null; diff --git a/core/src/main/java/io/cloudevents/core/v03/CloudEventBuilder.java b/core/src/main/java/io/cloudevents/core/v03/CloudEventBuilder.java index c120e259..1774ae73 100644 --- a/core/src/main/java/io/cloudevents/core/v03/CloudEventBuilder.java +++ b/core/src/main/java/io/cloudevents/core/v03/CloudEventBuilder.java @@ -28,9 +28,10 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; /** - * The event builder. + * CloudEvent V0.3 builder. * * @author fabiojose + * @author slinkydeveloper */ public final class CloudEventBuilder extends BaseCloudEventBuilder { diff --git a/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java b/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java index 61a71272..6989aa89 100644 --- a/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java +++ b/core/src/main/java/io/cloudevents/core/v1/CloudEventBuilder.java @@ -30,9 +30,10 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; /** + * CloudEvent V1.0 builder. + * * @author fabiojose * @author slinkydeveloper - * @version 1.0 */ public final class CloudEventBuilder extends BaseCloudEventBuilder { diff --git a/core/src/main/java/io/cloudevents/core/v1/CloudEventV1.java b/core/src/main/java/io/cloudevents/core/v1/CloudEventV1.java index 90ae9cb9..60463681 100644 --- a/core/src/main/java/io/cloudevents/core/v1/CloudEventV1.java +++ b/core/src/main/java/io/cloudevents/core/v1/CloudEventV1.java @@ -28,9 +28,10 @@ import java.util.Map; import java.util.Objects; /** + * CloudEvent implementation for v1.0 + * * @author fabiojose * @author slinkydeveloper - * @version 1.0 */ public final class CloudEventV1 extends BaseCloudEvent { diff --git a/core/src/test/java/io/cloudevents/core/mock/CSVFormat.java b/core/src/test/java/io/cloudevents/core/mock/CSVFormat.java index 4dc54715..2ed0e95e 100644 --- a/core/src/test/java/io/cloudevents/core/mock/CSVFormat.java +++ b/core/src/test/java/io/cloudevents/core/mock/CSVFormat.java @@ -57,8 +57,8 @@ public class CSVFormat implements EventFormat { } @Override - public CloudEvent deserialize(byte[] event) { - String[] splitted = new String(event, StandardCharsets.UTF_8).split(Pattern.quote(",")); + public CloudEvent deserialize(byte[] bytes) { + String[] splitted = new String(bytes, StandardCharsets.UTF_8).split(Pattern.quote(",")); SpecVersion sv = SpecVersion.parse(splitted[0]); String id = splitted[1]; diff --git a/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java b/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java index 9653503b..9a811708 100644 --- a/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java +++ b/formats/json-jackson/src/main/java/io/cloudevents/jackson/JsonFormat.java @@ -69,9 +69,9 @@ public final class JsonFormat implements EventFormat { } @Override - public CloudEvent deserialize(byte[] event) throws EventDeserializationException { + public CloudEvent deserialize(byte[] bytes) throws EventDeserializationException { try { - return mapper.readValue(event, CloudEvent.class); + return mapper.readValue(bytes, CloudEvent.class); } catch (IOException e) { throw new EventDeserializationException(e); }