Javadocs api and core (#313)

* Javadocs!!!

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

* Missing module name?

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

* Excluding javadocs

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

* clean install only release artifacts, but verify them all!

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

* Reverted the crazy idea to use the release profile

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

* Suggestions

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

* Suggestion

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

* Nit

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

* Nit

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

* Nit

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
Francesco Guardiani 2020-12-09 18:31:56 +01:00 committed by GitHub
parent 59643c3368
commit f5d9b47c1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 340 additions and 123 deletions

View File

@ -16,8 +16,10 @@
<properties>
<protonj.version>0.33.7</protonj.version>
<jsr305.version>3.0.2</jsr305.version>
<module-name>io.cloudevents.amqp.proton</module-name>
</properties>
<dependencies>
<dependencies>
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>proton-j</artifactId>

View File

@ -20,12 +20,12 @@ import io.cloudevents.lang.Nullable;
/**
* Interface representing an in memory read only representation of a CloudEvent,
* as specified by the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md">CloudEvents specification</a>
* as specified by the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md">CloudEvents specification</a>.
*/
public interface CloudEvent extends CloudEventContext {
/**
* The event data
* @return The event data, if any, otherwise {@code null}
*/
@Nullable
CloudEventData getData();

View File

@ -29,11 +29,17 @@ import java.util.stream.Stream;
*/
@ParametersAreNonnullByDefault
public enum SpecVersion {
/**
* @see <a href="https://github.com/cloudevents/spec/releases/tag/v0.3">CloudEvents release v0.3</a>
*/
V03(
"0.3",
Arrays.asList("specversion", "id", "type", "source"),
Arrays.asList("datacontenttype", "datacontentencoding", "schemaurl", "subject", "time")
),
/**
* @see <a href="https://github.com/cloudevents/spec/releases/tag/v1.0">CloudEvents release v1.0</a>
*/
V1(
"1.0",
Arrays.asList("specversion", "id", "type", "source"),

View File

@ -27,6 +27,9 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static javax.annotation.meta.When.MAYBE;
/**
* This annotation is used to define a method parameter or return type as nullable.
*/
@Target(value = {METHOD, PARAMETER, FIELD})
@Retention(value = RUNTIME)
@Documented

View File

@ -23,6 +23,8 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface to convert a {@link CloudEventData} instance to another one.
*
* @param <R> the returned {@link CloudEventData} from this mapper.
*/
@FunctionalInterface
@ParametersAreNonnullByDefault
@ -38,7 +40,7 @@ public interface CloudEventDataMapper<R extends CloudEventData> {
R map(CloudEventData data) throws CloudEventRWException;
/**
* No-op identity mapper which can be used as default when no mapper is provided.
* @return No-op identity mapper which can be used as default when no mapper is provided.
*/
static CloudEventDataMapper<CloudEventData> identity() {
return d -> d;

View File

@ -17,6 +17,8 @@
package io.cloudevents.rw;
import io.cloudevents.lang.Nullable;
/**
* This class is the exception Protocol Binding and Event Format implementers can use to signal errors while serializing/deserializing CloudEvent.
*/
@ -35,9 +37,9 @@ public class CloudEventRWException extends RuntimeException {
*/
INVALID_ATTRIBUTE_NAME,
/**
* The extension name is not valid,
* because it doesn't follow the <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#attribute-naming-convention">naming convention</a>
* enforced by the CloudEvents spec.
* The extension name is not valid because it doesn't follow the naming convention enforced by the CloudEvents spec.
*
* @see <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#attribute-naming-convention">naming convention</a>
*/
INVALID_EXTENSION_NAME,
/**
@ -83,10 +85,17 @@ public class CloudEventRWException extends RuntimeException {
this.kind = kind;
}
/**
* @return the {@link CloudEventRWExceptionKind} associated to this exception instance.
*/
public CloudEventRWExceptionKind getKind() {
return kind;
}
/**
* @param specVersion the invalid input spec version
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidSpecVersion(String specVersion) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_SPEC_VERSION,
@ -94,6 +103,10 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param attributeName the invalid attribute name
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeName(String attributeName) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_NAME,
@ -101,6 +114,10 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param extensionName the invalid extension name
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidExtensionName(String extensionName) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_EXTENSION_NAME,
@ -108,6 +125,11 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param attributeName the invalid attribute name
* @param clazz the type of the attribute
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeType(String attributeName, Class<?> clazz) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_TYPE,
@ -115,7 +137,13 @@ public class CloudEventRWException extends RuntimeException {
);
}
public static CloudEventRWException newInvalidAttributeValue(String attributeName, Object value, Throwable cause) {
/**
* @param attributeName the invalid attribute name
* @param value the value of the attribute
* @param cause an optional cause identifying the eventual validation error
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidAttributeValue(String attributeName, Object value, @Nullable Throwable cause) {
return new CloudEventRWException(
CloudEventRWExceptionKind.INVALID_ATTRIBUTE_VALUE,
"Invalid attribute/extension value for \"" + attributeName + "\": " + value,
@ -123,6 +151,11 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param actual the actual data type
* @param allowed the list of allowed data types
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newInvalidDataType(String actual, String... allowed) {
String message;
if (allowed.length == 0) {
@ -136,6 +169,12 @@ public class CloudEventRWException extends RuntimeException {
);
}
/**
* @param cause the cause of the conversion failure
* @param from the input data type
* @param to the target data type
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newDataConversion(Throwable cause, String from, String to) {
return new CloudEventRWException(
CloudEventRWExceptionKind.DATA_CONVERSION,
@ -144,17 +183,26 @@ public class CloudEventRWException extends RuntimeException {
);
}
public static CloudEventRWException newOther(Throwable cause) {
return new CloudEventRWException(
CloudEventRWExceptionKind.OTHER,
cause
);
}
/**
* @return a new {@link CloudEventRWException} instance.
*/
public static CloudEventRWException newUnknownEncodingException() {
return new CloudEventRWException(
CloudEventRWExceptionKind.UNKNOWN_ENCODING,
"Could not parse. Unknown encoding. Invalid content type or spec version"
);
}
/**
* This wraps a {@link Throwable} in a new generic instance of this exception.
*
* @param cause the cause of the exception
* @return a new {@link CloudEventRWException} instance
*/
public static CloudEventRWException newOther(Throwable cause) {
return new CloudEventRWException(
CloudEventRWExceptionKind.OTHER,
cause
);
}
}

View File

@ -30,18 +30,24 @@ import javax.annotation.ParametersAreNonnullByDefault;
public interface CloudEventReader {
/**
* Visit self using the provided visitor factory
* Like {@link #read(CloudEventWriterFactory, CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @throws CloudEventRWException if something went wrong during the read.
* @see #read(CloudEventWriterFactory, CloudEventDataMapper)
*/
default <V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory) throws CloudEventRWException {
default <W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory) throws CloudEventRWException {
return read(writerFactory, CloudEventDataMapper.identity());
}
/**
* Like {@link CloudEventReader#read(CloudEventWriterFactory)}, but providing a mapper for {@link io.cloudevents.CloudEventData} to be invoked when the data field is available.
* Read self using the provided writer factory.
*
* @param <W> the {@link CloudEventWriter} type
* @param <R> the return type of the {@link CloudEventWriter}
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @param mapper the mapper to invoke when building the {@link CloudEventData}
* @return the value returned by {@link CloudEventWriter#end()} or {@link CloudEventWriter#end(CloudEventData)}
* @throws CloudEventRWException if something went wrong during the read.
*/
<V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException;
<W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException;
}

View File

@ -28,15 +28,16 @@ import io.cloudevents.CloudEventData;
public interface CloudEventWriter<R> extends CloudEventContextWriter {
/**
* End the visit with a data field
* End the write with a data payload.
*
* @param data the data to write
* @return an eventual return value
* @throws CloudEventRWException if the message writer cannot be ended.
*/
R end(CloudEventData data) throws CloudEventRWException;
/**
* End the visit
* End the write.
*
* @return an eventual return value
* @throws CloudEventRWException if the message writer cannot be ended.

View File

@ -19,8 +19,14 @@ package io.cloudevents.rw;
import io.cloudevents.SpecVersion;
/**
* This factory is used to enforce setting the {@link SpecVersion} as first step in the writing process.
*
* @param <W> The type of the {@link CloudEventWriter} created by this factory
* @param <R> The return value of the {@link CloudEventWriter} created by this factory
*/
@FunctionalInterface
public interface CloudEventWriterFactory<V extends CloudEventWriter<R>, R> {
public interface CloudEventWriterFactory<W extends CloudEventWriter<R>, R> {
/**
* Create a {@link CloudEventWriter} starting from the provided {@link SpecVersion}
@ -29,5 +35,5 @@ public interface CloudEventWriterFactory<V extends CloudEventWriter<R>, R> {
* @return the new writer
* @throws CloudEventRWException if the spec version is invalid or the writer cannot be instantiated.
*/
V create(SpecVersion version) throws CloudEventRWException;
W create(SpecVersion version) throws CloudEventRWException;
}

View File

@ -34,6 +34,7 @@
<javac.target>1.8</javac.target>
<!-- Name of the benchmark Uber-JAR to generate. -->
<uberjar.name>benchmarks</uberjar.name>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>

View File

@ -30,7 +30,7 @@ import io.cloudevents.rw.CloudEventRWException;
import io.cloudevents.rw.CloudEventReader;
/**
* This class contains a set of utility methods to deal with conversions of io.cloudevents related interfaces
* This class contains a set of utility methods to deal with conversions of {@link io.cloudevents} related interfaces
*/
public final class CloudEventUtils {
@ -38,8 +38,7 @@ public final class CloudEventUtils {
/**
* Convert a {@link CloudEvent} to a {@link CloudEventReader}. This method provides a default implementation
* for CloudEvent that doesn't implement {@link CloudEventReader}
* for CloudEvent that doesn't implement CloudEventVisitable.
* for CloudEvent that doesn't implement {@link CloudEventReader}.
* <p>
* It's safe to use the returned {@link CloudEventReader} multiple times.
*
@ -94,6 +93,11 @@ public final class CloudEventUtils {
/**
* Get the data contained in {@code event} and map it using the provided mapper.
*
* @param event the event eventually containing the data
* @param mapper the mapper to use to map the data
* @param <R> the returned {@link CloudEventData} implementation from the provided mapper
* @return the data contained in {@code event} and mapped with {@code mapper}, if any, otherwise null
*/
@Nullable
public static <R extends CloudEventData> R mapData(CloudEvent event, CloudEventDataMapper<R> mapper) {

View File

@ -5,11 +5,15 @@ import io.cloudevents.CloudEventData;
import java.util.Arrays;
import java.util.Objects;
/**
* An implementation of {@link CloudEventData} that wraps a byte array.
*/
public class BytesCloudEventData implements CloudEventData {
private final byte[] value;
/**
* @param value the bytes to wrap
* @deprecated use {@link BytesCloudEventData#wrap(byte[])}
*/
public BytesCloudEventData(byte[] value) {

View File

@ -5,6 +5,11 @@ import io.cloudevents.rw.CloudEventRWException;
import java.util.Objects;
/**
* An implementation of {@link CloudEventData} that wraps any POJO.
*
* @param <T> the type of the wrapped POJO.
*/
public class PojoCloudEventData<T> implements CloudEventData {
/**
@ -15,6 +20,11 @@ public class PojoCloudEventData<T> implements CloudEventData {
*/
@FunctionalInterface
public interface ToBytes<T> {
/**
* @param data the POJO to convert
* @return the serialized byte array.
* @throws Exception when something goes wrong during the conversion.
*/
byte[] convert(T data) throws Exception;
}
@ -36,6 +46,9 @@ public class PojoCloudEventData<T> implements CloudEventData {
this.mapper = mapper;
}
/**
* @return the wrapped POJO
*/
public T getValue() {
return value;
}
@ -67,6 +80,11 @@ public class PojoCloudEventData<T> implements CloudEventData {
/**
* Wrap the provided data in a {@link PojoCloudEventData} serializable by the provided mapper.
*
* @param <T> The type of {@code data}
* @param data the POJO to wrap
* @param mapper converter from {@code data} to bytes, used to implement {@link #toBytes()}
* @return the new {@link PojoCloudEventData}
*/
public static <T> PojoCloudEventData<T> wrap(T data, ToBytes<T> mapper) {
return new PojoCloudEventData<>(data, mapper);

View File

@ -20,6 +20,7 @@ package io.cloudevents.core.extensions;
import io.cloudevents.CloudEventExtensions;
import io.cloudevents.Extension;
import io.cloudevents.core.extensions.impl.ExtensionUtils;
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
@ -27,19 +28,30 @@ import java.util.Set;
/**
* This extension supports the "Claim Check Pattern". It allows to specify a reference to a location where the event payload is stored.
*
* @see <a href=https://github.com/cloudevents/spec/blob/v1.0/extensions/dataref.md>https://github.com/cloudevents/spec/blob/v1.0/extensions/dataref.md</a>
*/
public final class DatarefExtension implements Extension {
/**
* The key of the {@code dataref} extension
*/
public static final String DATAREF = "dataref";
private static final Set<String> KEY_SET = Collections.unmodifiableSet(new HashSet<>(Collections.singletonList(DATAREF)));
private URI dataref;
/**
* @return the {@code dataref} contained in this extension.
*/
public URI getDataref() {
return dataref;
}
/**
* @param dataref the uri to set as {@code dataref}.
*/
public void setDataref(URI dataref) {
this.dataref = dataref;
}
@ -57,7 +69,7 @@ public final class DatarefExtension implements Extension {
if (DATAREF.equals(key)) {
return this.dataref.toString();
}
throw ExtensionUtils.generateInvalidKeyException(this.getClass().getSimpleName(), key);
throw ExtensionUtils.generateInvalidKeyException(this.getClass(), key);
}
@Override

View File

@ -21,10 +21,7 @@ import io.cloudevents.CloudEventExtensions;
import io.cloudevents.Extension;
import io.cloudevents.core.extensions.impl.ExtensionUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
/**
* This extension embeds context from Distributed Tracing so that distributed systems can include traces that span an event-driven system.
@ -33,25 +30,45 @@ import java.util.Set;
*/
public final class DistributedTracingExtension implements Extension {
/**
* The key of the {@code traceparent} extension
*/
public static final String TRACEPARENT = "traceparent";
/**
* The key of the {@code tracestate} extension
*/
public static final String TRACESTATE = "tracestate";
private static final Set<String> KEY_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(TRACEPARENT, TRACESTATE)));
private String traceparent;
private String tracestate;
/**
* @return the {@code traceparent} contained in this extension.
*/
public String getTraceparent() {
return traceparent;
}
/**
* @param traceparent the string to set as {@code traceparent}.
*/
public void setTraceparent(String traceparent) {
this.traceparent = traceparent;
}
/**
* @return the {@code tracestate} contained in this extension.
*/
public String getTracestate() {
return tracestate;
}
/**
* @param tracestate the string to set as {@code tracestate}.
*/
public void setTracestate(String tracestate) {
this.tracestate = tracestate;
}
@ -76,7 +93,7 @@ public final class DistributedTracingExtension implements Extension {
case TRACESTATE:
return this.tracestate;
}
throw ExtensionUtils.generateInvalidKeyException(this.getClass().getSimpleName(), key);
throw ExtensionUtils.generateInvalidKeyException(this.getClass(), key);
}
@Override
@ -93,35 +110,16 @@ public final class DistributedTracingExtension implements Extension {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((traceparent == null) ? 0
: traceparent.hashCode());
result = prime * result + ((tracestate == null) ? 0
: tracestate.hashCode());
return result;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DistributedTracingExtension that = (DistributedTracingExtension) o;
return Objects.equals(getTraceparent(), that.getTraceparent()) &&
Objects.equals(getTracestate(), that.getTracestate());
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DistributedTracingExtension other = (DistributedTracingExtension) obj;
if (traceparent == null) {
if (other.traceparent != null)
return false;
} else if (!traceparent.equals(other.traceparent))
return false;
if (tracestate == null) {
if (other.tracestate != null)
return false;
} else if (!tracestate.equals(other.tracestate))
return false;
return true;
public int hashCode() {
return Objects.hash(getTraceparent(), getTracestate());
}
}

View File

@ -17,13 +17,23 @@
package io.cloudevents.core.extensions.impl;
import io.cloudevents.Extension;
/**
* Collection of utilities to deal with materialized extensions
*/
public final class ExtensionUtils {
private ExtensionUtils() {
}
public static IllegalArgumentException generateInvalidKeyException(String extensionName, String key) {
return new IllegalArgumentException(extensionName + " doesn't expect the attribute key \"" + key + "\"");
/**
* @param clazz the {@link Extension} class
* @param key the invalid key
* @return an {@link IllegalArgumentException} when trying to access a key of the extension not existing.
*/
public static IllegalArgumentException generateInvalidKeyException(Class<? extends Extension> clazz, String key) {
return new IllegalArgumentException(clazz.getName() + " doesn't expect the attribute key \"" + key + "\"");
}
}

View File

@ -21,7 +21,10 @@ 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);
/**
* @param cause the cause of the exception
*/
public EventDeserializationException(Throwable cause) {
super(cause);
}
}

View File

@ -48,18 +48,21 @@ public interface EventFormat {
byte[] serialize(CloudEvent event) throws EventSerializationException;
/**
* Deserialize a byte array to a {@link CloudEvent}.
* Like {@link #deserialize(byte[], CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param bytes the serialized event.
* @return the deserialized event.
* @throws EventDeserializationException if something goes wrong during deserialization.
* @see #deserialize(byte[], CloudEventDataMapper)
*/
default CloudEvent deserialize(byte[] bytes) throws EventDeserializationException {
return this.deserialize(bytes, null);
return this.deserialize(bytes, CloudEventDataMapper.identity());
}
/**
* Like {@link EventFormat#deserialize(byte[])}, but allows a mapper that maps the parsed {@link io.cloudevents.CloudEventData} to another one.
* Deserialize a byte array to a {@link CloudEvent}.
*
* @param bytes the serialized event.
* @param mapper the mapper to use to map the data.
* @return the deserialized event.
* @throws EventDeserializationException if something goes wrong during deserialization.
*/
CloudEvent deserialize(byte[] bytes, CloudEventDataMapper<? extends CloudEventData> mapper) throws EventDeserializationException;

View File

@ -21,7 +21,10 @@ 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);
/**
* @param cause the cause of the exception
*/
public EventSerializationException(Throwable cause) {
super(cause);
}
}

View File

@ -181,10 +181,11 @@ public abstract class BaseCloudEventBuilder<SELF extends BaseCloudEventBuilder<S
return new IllegalStateException("Attribute '" + attributeName + "' cannot be null");
}
/**
* Validates the extension name as defined in CloudEvents spec
* See <a href="https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-convention">attribute-naming-convention</a>
* Validates the extension name as defined in CloudEvents spec.
*
* @param name the extension name
* @return true if extension name is valid, false otherwise
* @see <a href="https://github.com/cloudevents/spec/blob/master/spec.md#attribute-naming-convention">attribute-naming-convention</a>
*/
private static boolean isValidExtensionName(String name) {
if(name.length() > 20){

View File

@ -21,6 +21,12 @@ package io.cloudevents.core.message;
* One of the possible encodings of a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a>
*/
public enum Encoding {
/**
* Structured mode
*/
STRUCTURED,
/**
* Binary mode
*/
BINARY
}

View File

@ -19,41 +19,50 @@ package io.cloudevents.core.message;
import io.cloudevents.CloudEvent;
import io.cloudevents.CloudEventData;
import io.cloudevents.SpecVersion;
import io.cloudevents.core.CloudEventUtils;
import io.cloudevents.rw.*;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Represents a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a>.
* Represents a <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#message">CloudEvent message</a> reader.
* <p>
* This class expands the {@link CloudEventReader} to define reading both binary and structured messages.
*/
@ParametersAreNonnullByDefault
public interface MessageReader extends StructuredMessageReader, CloudEventReader {
/**
* Visit the message as binary encoded event using the provided visitor factory.
* Like {@link #read(CloudEventWriterFactory, CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @param writerFactory a factory that generates a visitor starting from the SpecVersion of the event
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in binary encoding.
* @see #read(CloudEventWriterFactory, CloudEventDataMapper)
*/
default <V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory) throws CloudEventRWException, IllegalStateException {
default <W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory) throws CloudEventRWException, IllegalStateException {
return read(writerFactory, CloudEventDataMapper.identity());
}
/**
* Like {@link MessageReader#read(CloudEventWriterFactory)}, but providing a mapper for {@link io.cloudevents.CloudEventData} to be invoked when the data field is available.
* Read the message as binary encoded message using the provided writer factory.
*
* @param <W> the {@link CloudEventWriter} type
* @param <R> the return type of the {@link CloudEventWriter}
* @param writerFactory a factory that generates a reader starting from the {@link SpecVersion} of the event
* @param mapper the mapper to use to map the data, if any.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in binary encoding.
*/
<V extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<V, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException;
<W extends CloudEventWriter<R>, R> R read(CloudEventWriterFactory<W, R> writerFactory, CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException;
/**
* Visit the message as structured encoded event using the provided visitor
* Read the message as structured encoded message using the provided writer
*
* @param visitor Structured Message visitor
* @param <R> the return type of the {@link StructuredMessageWriter}
* @param writer Structured Message writer
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message is not in structured encoding.
* @throws IllegalStateException if the message is not in structured encoding.
*/
<T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException;
<R> R read(StructuredMessageWriter<R> writer) throws CloudEventRWException, IllegalStateException;
/**
* @return The message encoding
@ -64,38 +73,41 @@ public interface MessageReader extends StructuredMessageReader, CloudEventReader
* Read the content of this object using a {@link MessageWriter}. This method allows to transcode an event from one transport to another without
* converting it to {@link CloudEvent}. The resulting encoding will be the same as the original encoding.
*
* @param visitor the MessageVisitor accepting this Message
* @return The return value of the MessageVisitor
* @param <BW> the {@link CloudEventWriter} type
* @param <R> the return type of both {@link CloudEventWriter} and {@link StructuredMessageWriter}
* @param writer the {@link MessageWriter} accepting this Message
* @return The return value of the {@link MessageWriter}
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message has an unknown encoding.
*/
default <BV extends CloudEventWriter<R>, R> R read(MessageWriter<BV, R> visitor) throws CloudEventRWException, IllegalStateException {
default <BW extends CloudEventWriter<R>, R> R read(MessageWriter<BW, R> writer) throws CloudEventRWException, IllegalStateException {
switch (getEncoding()) {
case BINARY:
return this.read((CloudEventWriterFactory<BV, R>) visitor);
return this.read((CloudEventWriterFactory<BW, R>) writer);
case STRUCTURED:
return this.read((StructuredMessageWriter<R>) visitor);
return this.read((StructuredMessageWriter<R>) writer);
default:
throw new IllegalStateException("Unknown encoding");
throw new IllegalStateException(
"The provided Encoding doesn't exist. Please make sure your io.cloudevents deps versions are aligned."
);
}
}
/**
* Translate this message into a {@link CloudEvent} representation.
* Like {@link #toEvent(CloudEventDataMapper)}, but with the identity {@link CloudEventDataMapper}.
*
* @return A {@link CloudEvent} with the contents of this message.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws IllegalStateException if the message has an unknown encoding.
* @see #toEvent(CloudEventDataMapper)
*/
default CloudEvent toEvent() throws CloudEventRWException, IllegalStateException {
return toEvent(CloudEventDataMapper.identity());
}
/**
* Translate this message into a {@link CloudEvent} representation.
* Translate this message into a {@link CloudEvent} representation, mapping the data with the provided {@code mapper}.
*
* @param mapper the mapper to use to map the data, if any.
* @return A {@link CloudEvent} with the contents of this message.
* @throws CloudEventRWException if something went wrong during the visit.
* @throws CloudEventRWException if something went wrong during the read.
* @throws IllegalStateException if the message has an unknown encoding.
*/
default CloudEvent toEvent(CloudEventDataMapper<? extends CloudEventData> mapper) throws CloudEventRWException, IllegalStateException {
@ -105,7 +117,9 @@ public interface MessageReader extends StructuredMessageReader, CloudEventReader
case STRUCTURED:
return this.read((format, value) -> format.deserialize(value, mapper));
default:
throw new IllegalStateException("Unknown encoding");
throw new IllegalStateException(
"The provided Encoding doesn't exist. Please make sure your io.cloudevents deps versions are aligned."
);
}
}

View File

@ -34,11 +34,15 @@ import javax.annotation.ParametersAreNonnullByDefault;
public interface StructuredMessageReader {
/**
* @param visitor
* Read self using the provided writer.
*
* @param <R> the return type of the {@link StructuredMessageWriter}
* @param writer the writer to use to write out the message
* @return the return value returned by {@link StructuredMessageWriter#setEvent(EventFormat, byte[])}
* @throws CloudEventRWException If something went wrong when
* @throws IllegalStateException If the message is not a valid structured message
*/
<T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException;
<R> R read(StructuredMessageWriter<R> writer) throws CloudEventRWException, IllegalStateException;
default CloudEvent toEvent() throws CloudEventRWException, IllegalStateException {
return this.read(EventFormat::deserialize);
@ -49,9 +53,9 @@ public interface StructuredMessageReader {
}
/**
* Create a generic structured message from a {@link CloudEvent}
* Create a generic structured message from a {@link CloudEvent}.
*
* @param event
* @param event the event to convert to {@link StructuredMessageReader}
* @param contentType content type to use to resolve the {@link EventFormat}
* @return null if format was not found, otherwise returns the built message
*/
@ -60,10 +64,10 @@ public interface StructuredMessageReader {
}
/**
* Create a generic structured message from a {@link CloudEvent}
* Create a generic structured message from a {@link CloudEvent}.
*
* @param event
* @param format
* @param event the event to convert to {@link StructuredMessageReader}
* @param format the format to use to perform the conversion
* @return null if format was not found, otherwise returns the built message
*/
static StructuredMessageReader from(CloudEvent event, EventFormat format) {

View File

@ -25,15 +25,15 @@ import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface to write the {@link MessageReader} content (CloudEvents attributes, extensions and payload) to a new representation structured representation.
*
* @param <T> return value at the end of the write process.
* @param <R> return value at the end of the write process.
*/
@ParametersAreNonnullByDefault
@FunctionalInterface
public interface StructuredMessageWriter<T> {
public interface StructuredMessageWriter<R> {
/**
* Write an event using the provided {@link EventFormat}.
*/
T setEvent(EventFormat format, byte[] value) throws CloudEventRWException;
R setEvent(EventFormat format, byte[] value) throws CloudEventRWException;
}

View File

@ -22,6 +22,9 @@ import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.message.StructuredMessageWriter;
import io.cloudevents.rw.CloudEventRWException;
/**
* Base {@link MessageReader} implementation for a binary message
*/
public abstract class BaseBinaryMessageReader implements MessageReader {
@Override
@ -30,7 +33,7 @@ public abstract class BaseBinaryMessageReader implements MessageReader {
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
throw MessageUtils.generateWrongEncoding(Encoding.STRUCTURED, Encoding.BINARY);
}
}

View File

@ -75,14 +75,35 @@ public abstract class BaseGenericBinaryMessageReaderImpl<HK, HV> extends BaseBin
return visitor.end();
}
/**
* @param key header key
* @return true if this header is the content type header, false otherwise
*/
protected abstract boolean isContentTypeHeader(HK key);
/**
* @param key header key
* @return true if this header is a CloudEvents header, false otherwise
*/
protected abstract boolean isCloudEventsHeader(HK key);
/**
* @param key header key
* @return the key converted to a CloudEvents context attribute/extension name
*/
protected abstract String toCloudEventsKey(HK key);
/**
* Iterate over all the headers in the headers map.
*
* @param fn header consumer
*/
protected abstract void forEachHeader(BiConsumer<HK, HV> fn);
/**
* @param value header key
* @return the value converted to a valid CloudEvents attribute value as {@link String}.
*/
protected abstract String toCloudEventsValue(HV value);
}

View File

@ -24,6 +24,9 @@ import io.cloudevents.rw.CloudEventDataMapper;
import io.cloudevents.rw.CloudEventWriter;
import io.cloudevents.rw.CloudEventWriterFactory;
/**
* Base {@link MessageReader} implementation for a structured message
*/
public abstract class BaseStructuredMessageReader implements MessageReader {
@Override

View File

@ -24,6 +24,9 @@ import io.cloudevents.core.provider.EventFormatProvider;
import io.cloudevents.lang.Nullable;
import io.cloudevents.rw.CloudEventRWException;
/**
* Generic implementation of a structured message.
*/
public class GenericStructuredMessageReader extends BaseStructuredMessageReader {
private final EventFormat format;
@ -35,8 +38,8 @@ public class GenericStructuredMessageReader extends BaseStructuredMessageReader
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
return visitor.setEvent(format, payload);
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
return writer.setEvent(format, payload);
}
/**

View File

@ -22,6 +22,7 @@ import io.cloudevents.core.format.EventFormat;
import io.cloudevents.core.message.Encoding;
import io.cloudevents.core.message.MessageReader;
import io.cloudevents.core.provider.EventFormatProvider;
import io.cloudevents.rw.CloudEventRWException;
import java.util.Map;
import java.util.function.Function;
@ -31,17 +32,27 @@ import java.util.stream.Stream;
import static io.cloudevents.rw.CloudEventRWException.newUnknownEncodingException;
/**
* Collection of utilities useful to implement {@link MessageReader} and {@link io.cloudevents.core.message.MessageWriter} related code.
*/
public class MessageUtils {
/**
* Common flow to parse an incoming message that could be structured or binary.
*
* @param contentTypeHeaderReader supplier that returns the content type header, if any
* @param structuredMessageFactory factory to create the structured {@link MessageReader} from the provided {@link EventFormat}
* @param specVersionHeaderReader supplier that returns the spec version header, if any
* @param binaryMessageFactory factory to create the binary {@link MessageReader} from the provided {@link SpecVersion}
* @return the instantiated {@link MessageReader}
* @throws CloudEventRWException if something goes wrong while resolving the {@link SpecVersion} or if the message has unknown encoding
*/
public static MessageReader parseStructuredOrBinaryMessage(
Supplier<String> contentTypeHeaderReader,
Function<EventFormat, MessageReader> structuredMessageFactory,
Supplier<String> specVersionHeaderReader,
Function<SpecVersion, MessageReader> binaryMessageFactory
) {
) throws CloudEventRWException {
// Let's try structured mode
String ct = contentTypeHeaderReader.get();
if (ct != null) {
@ -49,7 +60,6 @@ public class MessageUtils {
if (format != null) {
return structuredMessageFactory.apply(format);
}
}
// Let's try binary mode
@ -77,6 +87,11 @@ public class MessageUtils {
.collect(Collectors.toMap(Function.identity(), headerNameMapping));
}
/**
* @param expected the expected encoding
* @param actual the actual encoding
* @return a new instance of {@link IllegalStateException}.
*/
public static IllegalStateException generateWrongEncoding(Encoding expected, Encoding actual) {
return new IllegalStateException("Cannot visit message as " + expected + " because the actual encoding is " + actual);
}

View File

@ -39,6 +39,9 @@ public final class ExtensionProvider {
private static final ExtensionProvider INSTANCE = new ExtensionProvider();
}
/**
* @return instance of {@link ExtensionProvider}
*/
public static ExtensionProvider getInstance() {
return SingletonContainer.INSTANCE;
}
@ -55,9 +58,9 @@ public final class ExtensionProvider {
/**
* Register a new extension type.
*
* @param <T> the type of the extension
* @param extensionClass the class implementing {@link Extension}
* @param factory the empty arguments factory
* @param <T> the type of the extension
* @param factory the empty arguments factory
*/
public <T extends Extension> void registerExtension(Class<T> extensionClass, Supplier<T> factory) {
this.extensionFactories.put(extensionClass, factory);
@ -66,9 +69,10 @@ public final class ExtensionProvider {
/**
* Parse an extension from the {@link CloudEventExtensions}, materializing the corresponding POJO.
*
* @param <T> the type of the extension
* @param extensionClass the class implementing {@link Extension}
* @param eventExtensions the event extensions to read
* @param <T> the type of the extension
* @return the parsed extension
*/
@SuppressWarnings("unchecked")
@Nullable

View File

@ -41,12 +41,12 @@ public class MockStructuredMessageReader extends BaseStructuredMessageReader imp
}
@Override
public <T> T read(StructuredMessageWriter<T> visitor) throws CloudEventRWException, IllegalStateException {
public <T> T read(StructuredMessageWriter<T> writer) throws CloudEventRWException, IllegalStateException {
if (this.format == null) {
throw new IllegalStateException("MockStructuredMessage is empty");
}
return visitor.setEvent(this.format, this.payload);
return writer.setEvent(this.format, this.payload);
}
@Override

View File

@ -13,6 +13,11 @@
<name>Cloudevents - Examples</name>
<packaging>pom</packaging>
<properties>
<!-- No need to generate javadocs for examples -->
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<modules>
<module>kafka</module>
<module>restful-ws-quarkus</module>

View File

@ -31,6 +31,11 @@
<name>CloudEvents - JAX-RS Web Http Binding Integration Tests</name>
<packaging>pom</packaging>
<properties>
<!-- No need to generate javadocs for these IT tests -->
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<modules>
<module>restful-ws-common</module>
<module>restful-ws-spring</module>

View File

@ -78,8 +78,8 @@
</modules>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Maven plugins -->
@ -155,7 +155,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<version>3.2.0</version>
<configuration>
<detectLinks/>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>