You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eventmesh.apache.org by mi...@apache.org on 2021/09/24 07:05:49 UTC

[incubator-eventmesh] branch develop updated: Implement the conversion from openmessaging to cloudevent (#514)

This is an automated email from the ASF dual-hosted git repository.

mikexue pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-eventmesh.git


The following commit(s) were added to refs/heads/develop by this push:
     new 537ed82  Implement the conversion from openmessaging to cloudevent (#514)
537ed82 is described below

commit 537ed8226f95337ddba770339387a8855bcb0a4d
Author: wangshaojie4039 <15...@163.com>
AuthorDate: Fri Sep 24 15:05:44 2021 +0800

    Implement the conversion from openmessaging to cloudevent (#514)
    
    * cloudevent commit
    
    * cloudevent commit
    
    * add cloudevent lib licenses
    
    Co-authored-by: wangshaojie <wa...@cmss.chinamobile.com>
---
 build.gradle                                       |   2 +
 eventmesh-runtime/build.gradle                     |   2 +-
 .../protocol/cloudevent/OMSMessageFactory.java     |  99 +++++++
 .../cloudevent/impl/OMSBinaryMessageReader.java    |  90 +++++++
 .../core/protocol/cloudevent/impl/OMSHeaders.java  |  44 ++++
 .../protocol/cloudevent/impl/OMSMessageWriter.java | 100 +++++++
 .../eventmesh/runtime/cloudevent/CSVFormat.java    | 107 ++++++++
 .../apache/eventmesh/runtime/cloudevent/Data.java  | 197 ++++++++++++++
 .../runtime/cloudevent/OMSFactoryTest.java         | 260 +++++++++++++++++++
 .../runtime/cloudevent/OMSWriterTest.java          | 287 +++++++++++++++++++++
 tool/license/allowed-licenses.txt                  |  10 +
 11 files changed, 1197 insertions(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index d42d81d..a8950a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -387,12 +387,14 @@ subprojects {
             dependency "org.powermock:powermock-module-junit4:2.0.2"
             dependency "org.powermock:powermock-api-mockito2:2.0.2"
 
+
             dependency 'org.springframework.boot:spring-boot-starter-data-jdbc:2.5.4'
             dependency 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.4'
             dependency 'org.springframework.boot:spring-boot-starter-jdbc:2.5.4'
             dependency 'org.springframework:spring-beans:5.1.8.RELEASE'
             dependency 'org.projectlombok:lombok:1.18.20'
             dependency 'com.h2database:h2:1.4.200'
+            dependency "io.cloudevents:cloudevents-core:2.2.0"
         }
     }
 }
\ No newline at end of file
diff --git a/eventmesh-runtime/build.gradle b/eventmesh-runtime/build.gradle
index 33d4452..b3d4d48 100644
--- a/eventmesh-runtime/build.gradle
+++ b/eventmesh-runtime/build.gradle
@@ -22,7 +22,7 @@ dependencies {
     api 'io.opentelemetry:opentelemetry-exporter-prometheus'
     api 'io.prometheus:simpleclient'
     api 'io.prometheus:simpleclient_httpserver'
-
+    api 'io.cloudevents:cloudevents-core'
     implementation project(":eventmesh-connector-plugin:eventmesh-connector-api")
     implementation project(":eventmesh-connector-plugin:eventmesh-connector-standalone")
     implementation project(":eventmesh-security-plugin:eventmesh-security-api")
diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/OMSMessageFactory.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/OMSMessageFactory.java
new file mode 100644
index 0000000..fcb2026
--- /dev/null
+++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/OMSMessageFactory.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.core.protocol.cloudevent;
+
+import io.cloudevents.core.message.MessageReader;
+import io.cloudevents.core.message.MessageWriter;
+import io.cloudevents.core.message.impl.GenericStructuredMessageReader;
+import io.cloudevents.core.message.impl.MessageUtils;
+import io.cloudevents.lang.Nullable;
+import io.cloudevents.rw.CloudEventRWException;
+import io.cloudevents.rw.CloudEventWriter;
+import io.openmessaging.api.Message;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.impl.OMSBinaryMessageReader;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.impl.OMSHeaders;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.impl.OMSMessageWriter;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+import java.util.Properties;
+
+/**
+ * This class provides a collection of methods to create {@link io.cloudevents.core.message.MessageReader}
+ * and {@link io.cloudevents.core.message.MessageWriter}
+ * manually serialize/deserialize {@link io.cloudevents.CloudEvent} messages.
+ */
+@ParametersAreNonnullByDefault
+public final class OMSMessageFactory {
+
+    private OMSMessageFactory() {
+        // prevent instantiation
+    }
+
+    /**
+     * create reader by message
+     * @param message
+     * @return
+     * @throws CloudEventRWException
+     */
+    public static MessageReader createReader(final Message message) throws CloudEventRWException {
+        return createReader(message.getUserProperties(), message.getBody());
+    }
+
+
+    public static MessageReader createReader(final Properties props, @Nullable final byte[] body) throws CloudEventRWException {
+
+        return MessageUtils.parseStructuredOrBinaryMessage(
+            () -> props.getOrDefault(OMSHeaders.CONTENT_TYPE,"").toString(),
+            format -> new GenericStructuredMessageReader(format, body),
+            () -> props.getOrDefault(OMSHeaders.SPEC_VERSION,"").toString(),
+            sv -> new OMSBinaryMessageReader(sv, props, body)
+        );
+    }
+
+
+    /**
+     * create writer by topic
+     * @param topic
+     * @return
+     */
+    public static MessageWriter<CloudEventWriter<Message>, Message> createWriter(String topic) {
+        return new OMSMessageWriter<>(topic);
+    }
+
+    /**
+     * create writer by topic,keys
+     * @param topic
+     * @param keys
+     * @return
+     */
+    public static MessageWriter<CloudEventWriter<Message>, Message> createWriter(String topic, String keys) {
+        return new OMSMessageWriter<>(topic, keys);
+    }
+
+    /**
+     * create writer by topic,keys,tags
+     * @param topic
+     * @param keys
+     * @param tags
+     * @return
+     */
+    public static MessageWriter<CloudEventWriter<Message>, Message> createWriter(String topic, String keys, String tags) {
+        return new OMSMessageWriter<>(topic, keys, tags);
+    }
+
+}
diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSBinaryMessageReader.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSBinaryMessageReader.java
new file mode 100644
index 0000000..ae035c7
--- /dev/null
+++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSBinaryMessageReader.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.core.protocol.cloudevent.impl;
+
+import io.cloudevents.SpecVersion;
+import io.cloudevents.core.data.BytesCloudEventData;
+import io.cloudevents.core.message.impl.BaseGenericBinaryMessageReaderImpl;
+
+import java.util.Objects;
+import java.util.Properties;
+import java.util.function.BiConsumer;
+
+/**
+ * binary message reader
+ */
+public class OMSBinaryMessageReader extends BaseGenericBinaryMessageReaderImpl<String, String> {
+
+    private final Properties headers;
+
+    public OMSBinaryMessageReader(SpecVersion version, Properties headers, byte[] payload) {
+        super(version, payload != null && payload.length > 0 ? BytesCloudEventData.wrap(payload) : null);
+
+        Objects.requireNonNull(headers);
+        this.headers = headers;
+    }
+
+    /**
+     * whether header key is content type
+     * @param key
+     * @return
+     */
+    @Override
+    protected boolean isContentTypeHeader(String key) {
+        return key.equals(OMSHeaders.CONTENT_TYPE);
+    }
+
+    /**
+     * whether message header is cloudEvent header
+     * @param key
+     * @return
+     */
+    @Override
+    protected boolean isCloudEventsHeader(String key) {
+        return key.length() > 3 && key.substring(0, OMSHeaders.CE_PREFIX.length()).startsWith(OMSHeaders.CE_PREFIX);
+    }
+
+    /**
+     * parse message header to cloudEvent attribute
+     * @param key
+     * @return
+     */
+    @Override
+    protected String toCloudEventsKey(String key) {
+        return key.substring(OMSHeaders.CE_PREFIX.length()).toLowerCase();
+    }
+
+    /**
+     *
+     * @param fn
+     */
+    @Override
+    protected void forEachHeader(BiConsumer<String, String> fn) {
+        this.headers.forEach((k, v) -> {
+            if (k != null && v != null) {
+                fn.accept(k.toString(), v.toString());
+            }
+
+        });
+    }
+
+    @Override
+    protected String toCloudEventsValue(String value) {
+        return value;
+    }
+}
diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSHeaders.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSHeaders.java
new file mode 100644
index 0000000..7747175
--- /dev/null
+++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSHeaders.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.core.protocol.cloudevent.impl;
+
+import io.cloudevents.core.message.impl.MessageUtils;
+import io.cloudevents.core.v1.CloudEventV1;
+
+import java.util.Map;
+
+/**
+ * Define the value of CE attribute in the header of ons
+ */
+public class OMSHeaders {
+
+    /**
+     * CE prefix
+     */
+    public static final String CE_PREFIX = "ce_";
+
+    /**
+     * Prefix each value
+     */
+    protected static final Map<String, String> ATTRIBUTES_TO_HEADERS = MessageUtils.generateAttributesToHeadersMapping(v -> CE_PREFIX + v);
+
+    public static final String CONTENT_TYPE = ATTRIBUTES_TO_HEADERS.get(CloudEventV1.DATACONTENTTYPE);
+
+    public static final String SPEC_VERSION = ATTRIBUTES_TO_HEADERS.get(CloudEventV1.SPECVERSION);
+
+}
+
diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSMessageWriter.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSMessageWriter.java
new file mode 100644
index 0000000..0d74331
--- /dev/null
+++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/core/protocol/cloudevent/impl/OMSMessageWriter.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.core.protocol.cloudevent.impl;
+
+import io.cloudevents.CloudEventData;
+import io.cloudevents.SpecVersion;
+import io.cloudevents.core.format.EventFormat;
+import io.cloudevents.core.message.MessageWriter;
+import io.cloudevents.rw.CloudEventContextWriter;
+import io.cloudevents.rw.CloudEventRWException;
+import io.cloudevents.rw.CloudEventWriter;
+import io.openmessaging.api.Message;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * write ce to ons
+ * @param <R>
+ */
+public final class OMSMessageWriter<R> implements MessageWriter<CloudEventWriter<Message>, Message>, CloudEventWriter<Message> {
+
+    private Message message;
+
+
+    public OMSMessageWriter(String topic) {
+        message = new Message();
+        message.setTopic(topic);
+    }
+
+    public OMSMessageWriter(String topic, String key) {
+        message = new Message();
+        message.setTopic(topic);
+        if (key != null && key.length() > 0) {
+            message.setKey(key);
+        }
+    }
+
+    public OMSMessageWriter(String topic, String key, String tag) {
+        message = new Message();
+        message.setTopic(topic);
+        if (StringUtils.isNotEmpty(tag)) {
+            message.setTag(tag);
+        }
+
+        if (StringUtils.isNotEmpty(key)) {
+            message.setKey(key);
+        }
+    }
+
+
+    @Override
+    public CloudEventContextWriter withContextAttribute(String name, String value) throws CloudEventRWException {
+
+        String propName = OMSHeaders.ATTRIBUTES_TO_HEADERS.get(name);
+        if (propName == null) {
+            propName = OMSHeaders.CE_PREFIX + name;
+        }
+        message.putUserProperties(propName, value);
+        return this;
+    }
+
+    @Override
+    public OMSMessageWriter<R> create(final SpecVersion version) {
+        message.putUserProperties(OMSHeaders.SPEC_VERSION, version.toString());
+        return this;
+    }
+
+    @Override
+    public Message setEvent(final EventFormat format, final byte[] value) throws CloudEventRWException {
+        message.putUserProperties(OMSHeaders.CONTENT_TYPE, format.serializedContentType());
+        message.setBody(value);
+        return message;
+    }
+
+    @Override
+    public Message end(final CloudEventData data) throws CloudEventRWException {
+        message.setBody(data.toBytes());
+        return message;
+    }
+
+    @Override
+    public Message end() {
+        message.setBody(null);
+        return message;
+    }
+}
diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/CSVFormat.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/CSVFormat.java
new file mode 100644
index 0000000..6a9d3c5
--- /dev/null
+++ b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/CSVFormat.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.cloudevent;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.SpecVersion;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import io.cloudevents.core.data.BytesCloudEventData;
+import io.cloudevents.core.format.EventFormat;
+import io.cloudevents.rw.CloudEventDataMapper;
+import io.cloudevents.types.Time;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.time.OffsetDateTime;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class CSVFormat implements EventFormat {
+
+    public static final CSVFormat INSTANCE = new CSVFormat();
+
+    @Override
+    public byte[] serialize(CloudEvent event) {
+        return String.join(
+            ",",
+            event.getSpecVersion().toString(),
+            event.getId(),
+            event.getType(),
+            event.getSource().toString(),
+            Objects.toString(event.getDataContentType()),
+            Objects.toString(event.getDataSchema()),
+            Objects.toString(event.getSubject()),
+            event.getTime() != null
+                ? Time.writeTime(event.getTime())
+                : "null",
+            event.getData() != null
+                ? new String(Base64.getEncoder().encode(event.getData().toBytes()), StandardCharsets.UTF_8)
+                : "null"
+        ).getBytes();
+    }
+
+    @Override
+    public CloudEvent deserialize(byte[] bytes, CloudEventDataMapper mapper) {
+        String[] splitted = new String(bytes, StandardCharsets.UTF_8).split(Pattern.quote(","));
+        SpecVersion sv = SpecVersion.parse(splitted[0]);
+
+        String id = splitted[1];
+        String type = splitted[2];
+        URI source = URI.create(splitted[3]);
+        String datacontenttype = splitted[4].equals("null") ? null : splitted[4];
+        URI dataschema = splitted[5].equals("null") ? null : URI.create(splitted[5]);
+        String subject = splitted[6].equals("null") ? null : splitted[6];
+        OffsetDateTime time = splitted[7].equals("null") ? null : Time.parseTime(splitted[7]);
+        byte[] data = splitted[8].equals("null") ? null : Base64.getDecoder().decode(splitted[8].getBytes());
+
+        CloudEventBuilder builder = CloudEventBuilder.fromSpecVersion(sv)
+            .withId(id)
+            .withType(type)
+            .withSource(source);
+
+        if (datacontenttype != null) {
+            builder.withDataContentType(datacontenttype);
+        }
+        if (dataschema != null) {
+            builder.withDataSchema(dataschema);
+        }
+        if (subject != null) {
+            builder.withSubject(subject);
+        }
+        if (time != null) {
+            builder.withTime(time);
+        }
+        if (data != null) {
+            builder.withData(mapper.map(BytesCloudEventData.wrap(data)));
+        }
+        return builder.build();
+    }
+
+    @Override
+    public Set<String> deserializableContentTypes() {
+        return Collections.singleton(serializedContentType());
+    }
+
+    @Override
+    public String serializedContentType() {
+        return "application/cloudevents+csv";
+    }
+}
diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/Data.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/Data.java
new file mode 100644
index 0000000..001aa19
--- /dev/null
+++ b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/Data.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.cloudevent;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+import io.cloudevents.types.Time;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.time.OffsetDateTime;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+public class Data {
+
+    public static final String ID = "1";
+    public static final String TYPE = "mock.test";
+    public static final URI SOURCE = URI.create("http://localhost/source");
+    public static final String DATACONTENTTYPE_JSON = "application/json";
+    public static final String DATACONTENTTYPE_XML = "application/xml";
+    public static final String DATACONTENTTYPE_TEXT = "text/plain";
+    public static final URI DATASCHEMA = URI.create("http://localhost/schema");
+    public static final String SUBJECT = "sub";
+    public static final OffsetDateTime TIME = Time.parseTime("2018-04-26T14:48:09+02:00");
+
+    protected static final byte[] DATA_JSON_SERIALIZED = "{}".getBytes();
+    protected static final byte[] DATA_XML_SERIALIZED = "<stuff></stuff>".getBytes();
+    protected static final byte[] DATA_TEXT_SERIALIZED = "Hello World Lorena!".getBytes();
+    protected static final byte[] BINARY_VALUE = { (byte) 0xE0, (byte) 0xFF, (byte) 0x00, (byte) 0x44, (byte) 0xAA }; // Base64: 4P8ARKo=
+
+    protected static final CloudEvent V1_MIN = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .build();
+
+    public static final CloudEvent V1_WITH_JSON_DATA = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(TIME)
+        .build();
+
+    public static final CloudEvent V1_WITH_JSON_DATA_WITH_FRACTIONAL_TIME = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(Time.parseTime("2018-04-26T14:48:09.1234Z"))
+        .build();
+
+    public static final CloudEvent V1_WITH_JSON_DATA_WITH_EXT = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(TIME)
+        .withExtension("astring", "aaa")
+        .withExtension("aboolean", true)
+        .withExtension("anumber", 10)
+        .build();
+
+    public static final CloudEvent V1_WITH_JSON_DATA_WITH_EXT_STRING = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(TIME)
+        .withExtension("astring", "aaa")
+        .withExtension("aboolean", "true")
+        .withExtension("anumber", "10")
+        .build();
+
+    public static final CloudEvent V1_WITH_XML_DATA = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_XML, DATA_XML_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(TIME)
+        .build();
+
+    public static final CloudEvent V1_WITH_TEXT_DATA = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withData(DATACONTENTTYPE_TEXT, DATA_TEXT_SERIALIZED)
+        .withSubject(SUBJECT)
+        .withTime(TIME)
+        .build();
+
+    public static final CloudEvent V1_WITH_BINARY_EXT = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withExtension("binary", BINARY_VALUE)
+        .build();
+
+    public static final CloudEvent V1_WITH_NUMERIC_EXT = CloudEventBuilder.v1()
+        .withId(ID)
+        .withType(TYPE)
+        .withSource(SOURCE)
+        .withExtension("integer", 42)
+        .withExtension("decimal", new BigDecimal("42.42"))
+        .withExtension("float", 4.2f)
+        .withExtension("long", new Long(4200))
+        .build();
+
+    public static final CloudEvent V03_MIN = CloudEventBuilder.v03(V1_MIN).build();
+    public static final CloudEvent V03_WITH_JSON_DATA = CloudEventBuilder.v03(V1_WITH_JSON_DATA).build();
+    public static final CloudEvent V03_WITH_JSON_DATA_WITH_EXT = CloudEventBuilder.v03(V1_WITH_JSON_DATA_WITH_EXT).build();
+    public static final CloudEvent V03_WITH_JSON_DATA_WITH_EXT_STRING = CloudEventBuilder.v03(V1_WITH_JSON_DATA_WITH_EXT_STRING).build();
+    public static final CloudEvent V03_WITH_XML_DATA = CloudEventBuilder.v03(V1_WITH_XML_DATA).build();
+    public static final CloudEvent V03_WITH_TEXT_DATA = CloudEventBuilder.v03(V1_WITH_TEXT_DATA).build();
+
+    public static Stream<CloudEvent> allEvents() {
+        return Stream.concat(v1Events(), v03Events());
+    }
+
+    public static Stream<CloudEvent> allEventsWithoutExtensions() {
+        return Stream.concat(v1Events(), v03Events()).filter(e -> e.getExtensionNames().isEmpty());
+    }
+
+    public static Stream<CloudEvent> allEventsWithStringExtensions() {
+        return Stream.concat(v1EventsWithStringExt(), v03EventsWithStringExt());
+    }
+
+    public static Stream<CloudEvent> v1Events() {
+        return Stream.of(
+            Data.V1_MIN,
+            Data.V1_WITH_JSON_DATA,
+            Data.V1_WITH_JSON_DATA_WITH_EXT,
+            Data.V1_WITH_XML_DATA,
+            Data.V1_WITH_TEXT_DATA
+        );
+    }
+
+    /**
+     * Due to the nature of CE there are scenarios where an event might be serialized
+     * in such a fashion that it can not be deserialized while retaining the orginal
+     * type information, this varies from format-2-format
+     */
+
+    public static Stream<CloudEvent> v1NonRoundTripEvents() {
+        return Stream.of(
+            Data.V1_WITH_BINARY_EXT
+        );
+    }
+
+    public static Stream<CloudEvent> v03Events() {
+        return Stream.of(
+            Data.V03_MIN,
+            Data.V03_WITH_JSON_DATA,
+            Data.V03_WITH_JSON_DATA_WITH_EXT,
+            Data.V03_WITH_XML_DATA,
+            Data.V03_WITH_TEXT_DATA
+        );
+    }
+
+    public static Stream<CloudEvent> v1EventsWithStringExt() {
+        return v1Events().map(ce -> {
+            io.cloudevents.core.v1.CloudEventBuilder builder = CloudEventBuilder.v1(ce);
+            ce.getExtensionNames().forEach(k -> builder.withExtension(k, Objects.toString(ce.getExtension(k))));
+            return builder.build();
+        });
+    }
+
+    public static Stream<CloudEvent> v03EventsWithStringExt() {
+        return v03Events().map(ce -> {
+            io.cloudevents.core.v03.CloudEventBuilder builder = CloudEventBuilder.v03(ce);
+            ce.getExtensionNames().forEach(k -> builder.withExtension(k, Objects.toString(ce.getExtension(k))));
+            return builder.build();
+        });
+    }
+
+}
diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSFactoryTest.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSFactoryTest.java
new file mode 100644
index 0000000..583262e
--- /dev/null
+++ b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSFactoryTest.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.cloudevent;
+
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.SpecVersion;
+import io.cloudevents.core.message.Encoding;
+import io.cloudevents.core.message.MessageReader;
+import io.cloudevents.core.provider.EventFormatProvider;
+import io.cloudevents.core.v03.CloudEventV03;
+import io.cloudevents.core.v1.CloudEventV1;
+import io.cloudevents.types.Time;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.OMSMessageFactory;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.impl.OMSHeaders;
+import org.junit.Test;
+
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OMSFactoryTest {
+
+    private static final String PREFIX_TEMPLATE = OMSHeaders.CE_PREFIX + "%s";
+    private static final String DATACONTENTTYPE_NULL = null;
+    private static final byte[] DATAPAYLOAD_NULL = null;
+
+    @Test
+    public void readBinary() {
+        Stream<Arguments> argumentsStream=binaryTestArguments();
+        argumentsStream.forEach(argument -> {
+            if (argument.contentType != null) {
+                argument.props.put(OMSHeaders.CONTENT_TYPE, argument.contentType);
+            }
+            Properties properties = new Properties();
+            properties.putAll(argument.props);
+            final MessageReader reader = OMSMessageFactory.createReader(properties, argument.body);
+            assertThat(reader.getEncoding()).isEqualTo(Encoding.BINARY);
+            assertThat(reader.toEvent()).isEqualTo(argument.event);
+
+        });
+
+    }
+
+    @Test
+    public void readStructured() {
+        Stream<CloudEvent> cloudEventStream= Data.allEventsWithoutExtensions();
+        EventFormatProvider.getInstance().registerFormat(CSVFormat.INSTANCE);
+        cloudEventStream.forEach(event -> {
+            final String contentType = CSVFormat.INSTANCE.serializedContentType() + "; charset=utf8";
+            final byte[] contentPayload = CSVFormat.INSTANCE.serialize(event);
+            Properties properties = new Properties();
+            properties.put(OMSHeaders.CONTENT_TYPE, contentType);
+            final MessageReader reader = OMSMessageFactory.createReader(properties, contentPayload);
+            assertThat(reader.getEncoding()).isEqualTo(Encoding.STRUCTURED);
+            assertThat(reader.toEvent()).isEqualTo(event);
+        });
+    }
+
+    private Stream<Arguments> binaryTestArguments() {
+
+        return Stream.of(
+                // V03
+                new Arguments(
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property("ignored", "ignore")
+                        ),
+                        DATACONTENTTYPE_NULL,
+                        DATAPAYLOAD_NULL,
+                        Data.V03_MIN
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignore")
+                        ),
+                        Data.DATACONTENTTYPE_JSON,
+                        Data.DATA_JSON_SERIALIZED,
+                        Data.V03_WITH_JSON_DATA
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("astring", "aaa"),
+                                property("aboolean", "true"),
+                                property("anumber", "10"),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_JSON,
+                        Data.DATA_JSON_SERIALIZED,
+                        Data.V03_WITH_JSON_DATA_WITH_EXT_STRING
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_XML,
+                        Data.DATA_XML_SERIALIZED,
+                        Data.V03_WITH_XML_DATA
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_TEXT,
+                        Data.DATA_TEXT_SERIALIZED,
+                        Data.V03_WITH_TEXT_DATA
+                ),
+                // V1
+                new Arguments(
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property("ignored", "ignored")
+                        ),
+                        DATACONTENTTYPE_NULL,
+                        DATAPAYLOAD_NULL,
+                        Data.V1_MIN
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_JSON,
+                        Data.DATA_JSON_SERIALIZED,
+                        Data.V1_WITH_JSON_DATA
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("astring", "aaa"),
+                                property("aboolean", "true"),
+                                property("anumber", "10"),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_JSON,
+                        Data.DATA_JSON_SERIALIZED,
+                        Data.V1_WITH_JSON_DATA_WITH_EXT_STRING
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_XML,
+                        Data.DATA_XML_SERIALIZED,
+                        Data.V1_WITH_XML_DATA
+                ),
+                new Arguments(
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATACONTENTTYPE_TEXT,
+                        Data.DATA_TEXT_SERIALIZED,
+                        Data.V1_WITH_TEXT_DATA
+                )
+        );
+    }
+
+    private static final AbstractMap.SimpleEntry<String, String> property(final String name, final String value) {
+        return name.equalsIgnoreCase("ignored") ?
+                new AbstractMap.SimpleEntry<>(name, value) :
+                new AbstractMap.SimpleEntry<>(String.format(PREFIX_TEMPLATE, name), value);
+    }
+
+    @SafeVarargs
+    private static final Map<String, String> properties(final AbstractMap.SimpleEntry<String, String>... entries) {
+        return Stream.of(entries)
+                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
+
+    }
+
+
+    private class Arguments {
+        private  Map<String, String> props;
+        private  String contentType;
+        private  byte[] body;
+        private  CloudEvent event;
+
+        public Arguments(Map<String, String> props, String contentType, byte[] body, CloudEvent event) {
+            this.props = props;
+            this.contentType = contentType;
+            this.body = body;
+            this.event = event;
+        }
+    }
+}
diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSWriterTest.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSWriterTest.java
new file mode 100644
index 0000000..834f156
--- /dev/null
+++ b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/cloudevent/OMSWriterTest.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.eventmesh.runtime.cloudevent;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.SpecVersion;
+import io.cloudevents.core.message.StructuredMessageReader;
+import io.cloudevents.core.v03.CloudEventV03;
+import io.cloudevents.core.v1.CloudEventV1;
+import io.cloudevents.types.Time;
+import io.openmessaging.api.Message;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.OMSMessageFactory;
+import org.apache.eventmesh.runtime.core.protocol.cloudevent.impl.OMSHeaders;
+import org.junit.Test;
+
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OMSWriterTest {
+
+    private static final String PREFIX_TEMPLATE = OMSHeaders.CE_PREFIX + "%s";
+    private static final String DATACONTENTTYPE_NULL = null;
+    private static final byte[] DATAPAYLOAD_NULL = null;
+
+
+    @Test
+    public void testRequestWithStructured() {
+        Stream<CloudEvent> cloudEventStream = Data.allEventsWithoutExtensions();
+        //String expectedContentType = CSVFormat.INSTANCE.serializedContentType();
+        cloudEventStream.forEach(event -> {
+            byte[] expectedBuffer = CSVFormat.INSTANCE.serialize(event);
+
+            String topic = "test";
+            String keys = "keys";
+            String tags = "tags";
+
+            Message message = StructuredMessageReader
+                    .from(event, CSVFormat.INSTANCE)
+                    .read(OMSMessageFactory.createWriter(topic, keys, tags));
+
+            assertThat(message.getTopic())
+                    .isEqualTo(topic);
+            assertThat(message.getKey())
+                    .isEqualTo(keys);
+            assertThat(message.getTag())
+                    .isEqualTo(tags);
+            assertThat(message.getBody())
+                    .isEqualTo(expectedBuffer);
+        });
+
+    }
+
+    @Test
+    public void testRequestWithBinary() {
+        Stream<Arguments> argumentsStream = binaryTestArguments();
+        String topic = "test";
+        String keys = "keys";
+        String tags = "tags";
+        argumentsStream.forEach(argument -> {
+            Message message = OMSMessageFactory
+                    .createWriter(topic, keys, tags)
+                    .writeBinary(argument.cloudEvent);
+
+            assertThat(message.getTopic())
+                    .isEqualTo(topic);
+            assertThat(message.getKey())
+                    .isEqualTo(keys);
+            assertThat(message.getTag())
+                    .isEqualTo(tags);
+            assertThat(message.getBody())
+                    .isEqualTo(argument.body);
+            assertThat(message.getUserProperties()
+                    .keySet().containsAll(argument.properties.keySet()));
+            assertThat(message.getUserProperties()
+                    .values().containsAll(argument.properties.values()));
+
+
+        });
+
+    }
+
+    private Stream<Arguments> binaryTestArguments() {
+
+        return Stream.of(
+                // V03
+                new Arguments(
+                        Data.V03_MIN,
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property("ignored", "ignore")
+                        ),
+                        DATAPAYLOAD_NULL
+                ),
+                new Arguments(
+                        Data.V03_WITH_JSON_DATA,
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignore")
+                        ),
+                        Data.DATA_JSON_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V03_WITH_JSON_DATA_WITH_EXT_STRING,
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SCHEMAURL, Data.DATASCHEMA.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("astring", "aaa"),
+                                property("aboolean", "true"),
+                                property("anumber", "10"),
+                                property("ignored", "ignored")
+                        ),
+                        Data.DATA_JSON_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V03_WITH_XML_DATA,
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_XML_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V03_WITH_TEXT_DATA,
+                        properties(
+                                property(CloudEventV03.SPECVERSION, SpecVersion.V03.toString()),
+                                property(CloudEventV03.ID, Data.ID),
+                                property(CloudEventV03.TYPE, Data.TYPE),
+                                property(CloudEventV03.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV03.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV03.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_TEXT_SERIALIZED
+
+                ),
+                // V1
+                new Arguments(
+                        Data.V1_MIN,
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property("ignored", "ignored")
+                        ),
+
+                        DATAPAYLOAD_NULL
+
+                ),
+                new Arguments(
+                        Data.V1_WITH_JSON_DATA,
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_JSON_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V1_WITH_JSON_DATA_WITH_EXT_STRING,
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.DATASCHEMA, Data.DATASCHEMA.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("astring", "aaa"),
+                                property("aboolean", "true"),
+                                property("anumber", "10"),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_JSON_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V1_WITH_XML_DATA,
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_XML_SERIALIZED
+
+                ),
+                new Arguments(
+                        Data.V1_WITH_TEXT_DATA,
+                        properties(
+                                property(CloudEventV1.SPECVERSION, SpecVersion.V1.toString()),
+                                property(CloudEventV1.ID, Data.ID),
+                                property(CloudEventV1.TYPE, Data.TYPE),
+                                property(CloudEventV1.SOURCE, Data.SOURCE.toString()),
+                                property(CloudEventV1.SUBJECT, Data.SUBJECT),
+                                property(CloudEventV1.TIME, Time.writeTime(Data.TIME)),
+                                property("ignored", "ignored")
+                        ),
+
+                        Data.DATA_TEXT_SERIALIZED
+
+                )
+        );
+    }
+
+    private static final AbstractMap.SimpleEntry<String, String> property(final String name, final String value) {
+        return name.equalsIgnoreCase("ignored") ?
+                new AbstractMap.SimpleEntry<>(name, value) :
+                new AbstractMap.SimpleEntry<>(String.format(PREFIX_TEMPLATE, name), value);
+    }
+
+    @SafeVarargs
+    private static final Map<String, String> properties(final AbstractMap.SimpleEntry<String, String>... entries) {
+        return Stream.of(entries)
+                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
+
+    }
+
+    private class Arguments {
+        CloudEvent cloudEvent;
+        Map<String, String> properties;
+        byte[] body;
+
+        public Arguments(CloudEvent cloudEvent, Map<String, String> properties, byte[] body) {
+            this.cloudEvent = cloudEvent;
+            this.properties = properties;
+            this.body = body;
+        }
+
+
+    }
+}
diff --git a/tool/license/allowed-licenses.txt b/tool/license/allowed-licenses.txt
index aeb2980..976d6c3 100644
--- a/tool/license/allowed-licenses.txt
+++ b/tool/license/allowed-licenses.txt
@@ -879,6 +879,16 @@
             "moduleLicense": "Apache License, Version 2.0",
             "moduleVersion": "1.28",
             "moduleName": "org.yaml:snakeyaml"
+        },
+        {
+            "moduleLicense": "The Apache Software License, Version 2.0",
+            "moduleVersion": "2.2.0",
+            "moduleName": "io.cloudevents:cloudevents-api"
+        },
+        {
+            "moduleLicense": "The Apache Software License, Version 2.0",
+            "moduleVersion": "2.2.0",
+            "moduleName": "io.cloudevents:cloudevents-core"
         }
   ]
 }
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@eventmesh.apache.org
For additional commands, e-mail: commits-help@eventmesh.apache.org