org.occurrent.application.converter.xstream.XStreamCloudEventConverter Maven / Gradle / Ivy
Show all versions of cloudevent-converter-xstream Show documentation
/*
*
* Copyright 2021 Johan Haleby
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.occurrent.application.converter.xstream;
import com.thoughtworks.xstream.XStream;
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import org.occurrent.application.converter.CloudEventConverter;
import org.occurrent.application.converter.typemapper.CloudEventTypeGetter;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.OffsetDateTime;
import java.util.UUID;
import java.util.function.Function;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.ZoneOffset.UTC;
import static java.util.Objects.requireNonNull;
/**
* An {@link CloudEventConverter} that uses {@link XStream} to serialize a domain event into XML (content type {@value #DEFAULT_CONTENT_TYPE}) that is used as data in a {@link CloudEvent}.
*
* @param The type of your domain event(s) to convert
*/
public class XStreamCloudEventConverter implements CloudEventConverter {
private static final String DEFAULT_CONTENT_TYPE = "application/xml";
private static final Charset DEFAULT_CHARSET = UTF_8;
private final XStream xStream;
private final URI cloudEventSource;
private final Function idMapper;
private final CloudEventTypeGetter cloudEventTypeGetter;
private final Function timeMapper;
private final Function subjectMapper;
private final String contentType;
private final Charset charset;
/**
* Create a new instance of the {@link XStreamCloudEventConverter} that does the following:
*
* - Uses a random UUID as cloud event id
* - Uses the simple name of the domain event class as cloud event type.
* - Uses {@code OffsetDateTime.now(UTC)} as cloud event time
* - Uses charset UTF-8 when converting the domain event to/from XML
* - No subject
*
*
* See cloud event documentation for info on what the cloud event attributes mean.
* Use {@link Builder} for more advanced configuration.
*
* @param xStream The XStream instance to use
* @param cloudEventSource The cloud event source.
* @see Builder The Builder for more advanced configuration
*/
public XStreamCloudEventConverter(XStream xStream, URI cloudEventSource) {
this(xStream, cloudEventSource, defaultIdMapperFunction(), defaultCloudEventTypeGetter(), defaultTimeMapperFunction(), defaultSubjectMapperFunction(), DEFAULT_CONTENT_TYPE, DEFAULT_CHARSET);
}
private XStreamCloudEventConverter(XStream xStream, URI cloudEventSource, Function idMapper, CloudEventTypeGetter cloudEventTypeGetter, Function timeMapper, Function subjectMapper, String contentType, Charset charset) {
requireNonNull(xStream, XStream.class.getSimpleName() + " cannot be null");
requireNonNull(cloudEventSource, "cloudEventSource cannot be null");
requireNonNull(idMapper, "idMapper cannot be null");
requireNonNull(cloudEventTypeGetter, "cloudEventTypeGetter cannot be null");
requireNonNull(timeMapper, "timeMapper cannot be null");
requireNonNull(subjectMapper, "subjectMapper cannot be null");
requireNonNull(charset, Charset.class.getSimpleName() + " cannot be null");
this.xStream = xStream;
this.cloudEventSource = cloudEventSource;
this.idMapper = idMapper;
this.timeMapper = timeMapper;
this.subjectMapper = subjectMapper;
this.contentType = contentType;
this.charset = charset;
this.cloudEventTypeGetter = cloudEventTypeGetter;
}
/**
* Converts the {@code domainEvent} into a {@link CloudEvent} using {@link XStream}.
*
* @param domainEvent The domain event to convert
* @return A {@link CloudEvent} converted from the domainEvent
.
*/
@Override
public CloudEvent toCloudEvent(T domainEvent) {
requireNonNull(domainEvent, "Domain event cannot be null");
return CloudEventBuilder.v1()
.withId(idMapper.apply(domainEvent))
.withSource(cloudEventSource)
.withType(cloudEventTypeGetter.getCloudEventType(domainEvent))
.withTime(timeMapper.apply(domainEvent))
.withSubject(subjectMapper.apply(domainEvent))
.withDataContentType(contentType)
.withData(xStream.toXML(domainEvent).getBytes(charset))
.build();
}
/**
* Converts the {@link CloudEvent} back into a {@code domainEvent} using {@link XStream}.
*
* @param cloudEvent The cloud event to convert
* @return A domainEvent
converted from a {@link CloudEvent}.
*/
@SuppressWarnings("unchecked")
@Override
public T toDomainEvent(CloudEvent cloudEvent) {
return (T) xStream.fromXML(new String(requireNonNull(cloudEvent.getData()).toBytes(), charset));
}
@Override
public String getCloudEventType(Class extends T> type) {
return cloudEventTypeGetter.getCloudEventType(type);
}
public static final class Builder {
private final XStream xStream;
private final URI cloudEventSource;
private String contentType = DEFAULT_CONTENT_TYPE;
private Charset charset = DEFAULT_CHARSET;
private Function idMapper = defaultIdMapperFunction();
private CloudEventTypeGetter cloudEventTypeGetter = defaultCloudEventTypeGetter();
private Function timeMapper = defaultTimeMapperFunction();
private Function subjectMapper = defaultSubjectMapperFunction();
public Builder(XStream xStream, URI cloudEventSource) {
this.xStream = xStream;
this.cloudEventSource = cloudEventSource;
}
/**
* @param contentType Specify the content type to use in the generated cloud event
*/
public Builder contentType(String contentType) {
this.contentType = contentType;
return this;
}
/**
* @param idMapper A function that generates the cloud event id based on the domain event. By default, a random UUID is used.
*/
public Builder idMapper(Function idMapper) {
this.idMapper = idMapper;
return this;
}
/**
* @param cloudEventTypeGetter A function that generates the cloud event type based on the domain event. By default, the "simple name" of the domain event is used.
*/
public Builder typeGetter(CloudEventTypeGetter cloudEventTypeGetter) {
this.cloudEventTypeGetter = cloudEventTypeGetter;
return this;
}
/**
* @param timeMapper A function that generates the cloud event time based on the domain event. By default, {@code OffsetDateTime.now(UTC)} is always returned.
*/
public Builder timeMapper(Function timeMapper) {
this.timeMapper = timeMapper;
return this;
}
/**
* @param subjectMapper A function that generates the cloud event subject based on the domain event. By default, {@code null} is always returned.
*/
public Builder subjectMapper(Function subjectMapper) {
this.subjectMapper = subjectMapper;
return this;
}
/**
* @param charset Specify the charset that XStream should use when serializing the domain event to bytes that is stored as the cloud event data attribute.
*/
public Builder charset(Charset charset) {
this.charset = charset;
return this;
}
/**
* @return A {@link XStreamCloudEventConverter} instance with the configured settings
*/
public XStreamCloudEventConverter build() {
return new XStreamCloudEventConverter<>(xStream, cloudEventSource, idMapper, cloudEventTypeGetter, timeMapper, subjectMapper, contentType, charset);
}
}
private static Function defaultIdMapperFunction() {
return __ -> UUID.randomUUID().toString();
}
private static CloudEventTypeGetter defaultCloudEventTypeGetter() {
return Class::getSimpleName;
}
private static Function defaultTimeMapperFunction() {
return __ -> OffsetDateTime.now(UTC);
}
private static Function defaultSubjectMapperFunction() {
return __ -> null;
}
}