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

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


The following commit(s) were added to refs/heads/master by this push:
     new 80674cd20 [ISSUE #3375] Add eventmesh-storage-kafka module
     new 3173688bf Merge pull request #3376 from xwm1992/add-storage-kafka
80674cd20 is described below

commit 80674cd20950d8b7589a6a3c462729e92caceced
Author: xwm1992 <[email protected]>
AuthorDate: Thu Mar 9 14:41:22 2023 +0800

    [ISSUE #3375] Add eventmesh-storage-kafka module
---
 .../eventmesh-storage-kafka/build.gradle           |  47 +++++++
 .../eventmesh-storage-kafka/gradle.properties      |  20 +++
 .../eventmesh/storage/kafka/common/Constants.java  |  23 ++++
 .../storage/kafka/common/EventMeshConstants.java   |  27 ++++
 .../storage/kafka/config/ClientConfiguration.java  |  98 ++++++++++++++
 .../storage/kafka/consumer/ConsumerImpl.java       | 148 +++++++++++++++++++++
 .../storage/kafka/consumer/KafkaConsumerImpl.java  |  97 ++++++++++++++
 .../kafka/consumer/KafkaConsumerRunner.java        | 119 +++++++++++++++++
 .../storage/kafka/producer/KafkaProducerImpl.java  | 104 +++++++++++++++
 .../storage/kafka/producer/ProducerImpl.java       | 133 ++++++++++++++++++
 .../storage/StorageResourceServiceKafkaImpl.java   |  33 +++++
 .../org.apache.eventmesh.api.consumer.Consumer     |  16 +++
 .../org.apache.eventmesh.api.producer.Producer     |  16 +++
 ...he.eventmesh.api.storage.StorageResourceService |  16 +++
 .../src/main/resources/kafka-client.properties     | 135 +++++++++++++++++++
 .../kafka/config/ClientConfigurationTest.java      |  70 ++++++++++
 .../org.apache.io.openmessaging.producer.Producer  |  20 +++
 .../src/test/resources/kafka-client.properties     |  48 +++++++
 settings.gradle                                    |   2 +
 19 files changed, 1172 insertions(+)

diff --git a/eventmesh-storage/eventmesh-storage-kafka/build.gradle 
b/eventmesh-storage/eventmesh-storage-kafka/build.gradle
new file mode 100644
index 000000000..9f100e129
--- /dev/null
+++ b/eventmesh-storage/eventmesh-storage-kafka/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+configurations {
+    implementation.exclude group: 'ch.qos.logback', module: 'logback-classic'
+    implementation.exclude group: 'log4j', module: 'log4j'
+}
+
+dependencies {
+    implementation project(":eventmesh-storage:eventmesh-storage-api")
+    implementation project(":eventmesh-common")
+    // https://mavenlibs.com/maven/dependency/io.cloudevents/cloudevents-kafka
+    implementation group: 'io.cloudevents', name: 'cloudevents-kafka', 
version: '2.2.1'
+
+    // https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients
+    implementation 'org.apache.kafka:kafka-clients:3.0.0'
+
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
+    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
+
+    testImplementation project(":eventmesh-storage:eventmesh-storage-api")
+    testImplementation project(":eventmesh-common")
+
+    testImplementation "org.mockito:mockito-core"
+    testImplementation "org.powermock:powermock-module-junit4"
+    testImplementation "org.powermock:powermock-api-mockito2"
+
+    compileOnly 'org.projectlombok:lombok'
+    compileOnly 'com.google.code.findbugs:jsr305:3.0.1'
+    annotationProcessor 'org.projectlombok:lombok'
+
+    testCompileOnly 'org.projectlombok:lombok'
+    testAnnotationProcessor 'org.projectlombok:lombok'
+}
diff --git a/eventmesh-storage/eventmesh-storage-kafka/gradle.properties 
b/eventmesh-storage/eventmesh-storage-kafka/gradle.properties
new file mode 100644
index 000000000..84cc1ec8d
--- /dev/null
+++ b/eventmesh-storage/eventmesh-storage-kafka/gradle.properties
@@ -0,0 +1,20 @@
+# 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.
+#
+
+kafka_version=3.2.0
+
+pluginType=storage
+pluginName=kafka
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/Constants.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/Constants.java
new file mode 100644
index 000000000..3e6e4c800
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/Constants.java
@@ -0,0 +1,23 @@
+/*
+ * 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.storage.kafka.common;
+
+public class Constants {
+
+    public static final String BROADCAST_PREFIX = "broadcast-";
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/EventMeshConstants.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/EventMeshConstants.java
new file mode 100644
index 000000000..2bc2f3c04
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/common/EventMeshConstants.java
@@ -0,0 +1,27 @@
+/*
+ * 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.storage.kafka.common;
+
+public class EventMeshConstants {
+
+    public static final String EVENTMESH_CONF_FILE = "kafka-client.properties";
+
+    public static final int DEFAULT_TIMEOUT_IN_MILLISECONDS = 3000;
+
+    public static final String STORE_TIMESTAMP = "storetime";
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/config/ClientConfiguration.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/config/ClientConfiguration.java
new file mode 100644
index 000000000..3749e8157
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/config/ClientConfiguration.java
@@ -0,0 +1,98 @@
+/*
+ * 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.storage.kafka.config;
+
+import org.apache.eventmesh.common.config.Config;
+import org.apache.eventmesh.common.config.ConfigFiled;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Config(prefix = "eventMesh.server.kafka", path = 
"classPath://kafka-client.properties")
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ClientConfiguration {
+
+    @ConfigFiled(field = "namesrvAddr", notEmpty = true)
+    @Builder.Default
+    private String namesrvAddr = "";
+
+    @ConfigFiled(field = "username")
+    @Builder.Default
+    private String clientUserName = "username";
+
+    @ConfigFiled(field = "password")
+    @Builder.Default
+    private String clientPass = "password";
+
+    @ConfigFiled(field = "client.consumeThreadMin")
+    @Builder.Default
+    private Integer consumeThreadMin = 2;
+
+    @ConfigFiled(field = "client.consumeThreadMax")
+    @Builder.Default
+    private Integer consumeThreadMax = 2;
+
+    @ConfigFiled(field = "client.consumeThreadPoolQueueSize")
+    @Builder.Default
+    private Integer consumeQueueSize = 10000;
+
+    @ConfigFiled(field = "client.pullBatchSize")
+    @Builder.Default
+    private Integer pullBatchSize = 32;
+
+    @ConfigFiled(field = "client.ackwindow")
+    @Builder.Default
+    private Integer ackWindow = 1000;
+
+    @ConfigFiled(field = "client.pubwindow")
+    @Builder.Default
+    private Integer pubWindow = 100;
+
+    @ConfigFiled(field = "client.comsumeTimeoutInMin")
+    @Builder.Default
+    private long consumeTimeout = 0L;
+
+    @ConfigFiled(field = "client.pollNameServerInterval")
+    @Builder.Default
+    private Integer pollNameServerInterval = 10 * 1000;
+
+    @ConfigFiled(field = "client.heartbeatBrokerInterval")
+    @Builder.Default
+    private Integer heartbeatBrokerInterval = 30 * 1000;
+
+    @ConfigFiled(field = "client.rebalanceInterval")
+    @Builder.Default
+    private Integer rebalanceInterval = 20 * 1000;
+
+    @ConfigFiled(field = "cluster")
+    @Builder.Default
+    private String clusterName = "";
+
+    @ConfigFiled(field = "accessKey")
+    @Builder.Default
+    private String accessKey = "";
+
+    @ConfigFiled(field = "secretKey")
+    @Builder.Default
+    private String secretKey = "";
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/ConsumerImpl.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/ConsumerImpl.java
new file mode 100644
index 000000000..92a5b86ec
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/ConsumerImpl.java
@@ -0,0 +1,148 @@
+/*
+ * 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.storage.kafka.consumer;
+
+import org.apache.eventmesh.api.AbstractContext;
+import org.apache.eventmesh.api.EventListener;
+import org.apache.eventmesh.api.exception.StorageConnectorRuntimeException;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.common.TopicPartition;
+import org.apache.kafka.common.serialization.StringDeserializer;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.kafka.CloudEventDeserializer;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ConsumerImpl {
+
+    private final KafkaConsumer<String, CloudEvent> kafkaConsumer;
+    private final Properties properties;
+    private AtomicBoolean started = new AtomicBoolean(false);
+    private EventListener eventListener;
+    private KafkaConsumerRunner kafkaConsumerRunner;
+    private ExecutorService executorService;
+    private Set<String> topicsSet;
+
+    public ConsumerImpl(final Properties properties) {
+        // Setting the ClassLoader to null is necessary for Kafka consumer 
configuration
+        final ClassLoader original = 
Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(null);
+        
+        Properties props = new Properties();
+        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 
properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG));
+        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
StringDeserializer.class);
+        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
CloudEventDeserializer.class);
+        props.put(ConsumerConfig.GROUP_ID_CONFIG, 
properties.getProperty(ConsumerConfig.GROUP_ID_CONFIG));
+        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
+
+        this.properties = props;
+        this.kafkaConsumer = new KafkaConsumer<String, CloudEvent>(props);
+        kafkaConsumerRunner = new KafkaConsumerRunner(this.kafkaConsumer);
+        executorService = Executors.newFixedThreadPool(10);
+        topicsSet = new HashSet<>();
+
+        Thread.currentThread().setContextClassLoader(original);
+    }
+
+    public Properties attributes() {
+        return properties;
+    }
+
+    public void start() {
+        if (this.started.compareAndSet(false, true)) {
+            executorService.submit(kafkaConsumerRunner);
+        }
+    }
+
+
+    public synchronized void shutdown() {
+        if (this.started.compareAndSet(true, false)) {
+            // Shutdown the executor and interrupt any running tasks
+            kafkaConsumerRunner.shutdown();
+            executorService.shutdown();
+        }
+    }
+
+
+    public boolean isStarted() {
+        return this.started.get();
+    }
+
+
+    public boolean isClosed() {
+        return !this.isStarted();
+    }
+
+    public KafkaConsumer<String, CloudEvent> getKafkaConsumer() {
+        return kafkaConsumer;
+    }
+
+    public synchronized void subscribe(String topic) {
+        try {
+            // Get the current subscription
+            topicsSet.add(topic);
+            List<String> topics = new ArrayList<>(topicsSet);
+            this.kafkaConsumer.subscribe(topics);
+        } catch (Exception e) {
+            log.error("Error while subscribing the Kafka consumer to topic: ", 
e);
+            throw new StorageConnectorRuntimeException(
+                String.format("Kafka consumer can't attach to %s.", topic));
+        }
+    }
+
+    public synchronized void unsubscribe(String topic) {
+        try {
+            // Kafka will unsubscribe *all* topic if calling unsubscribe, so we
+            this.kafkaConsumer.unsubscribe();
+            topicsSet.remove(topic);
+            List<String> topics = new ArrayList<>(topicsSet);
+            this.kafkaConsumer.subscribe(topics);
+        } catch (Exception e) {
+            log.error("Error while unsubscribing the Kafka consumer: ", e);
+            throw new StorageConnectorRuntimeException(String.format("kafka 
push consumer fails to unsubscribe topic: %s", topic));
+        }
+    }
+
+    public void updateOffset(List<CloudEvent> cloudEvents, AbstractContext 
context) {
+        Long maxOffset = cloudEvents.stream().map(cloudEvent -> 
this.kafkaConsumerRunner.getOffset(cloudEvent)).max(Long::compare).get();
+        cloudEvents.forEach(cloudEvent -> 
this.updateOffset(cloudEvent.getSubject(), maxOffset));
+    }
+
+    public void updateOffset(String topicName, long offset) {
+        this.kafkaConsumer.seek(new TopicPartition(topicName, 1), offset);
+    }
+
+    public void registerEventListener(EventListener listener) {
+        this.eventListener = listener;
+        this.kafkaConsumerRunner.setListener(this.eventListener);
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerImpl.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerImpl.java
new file mode 100644
index 000000000..0c88ec934
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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.storage.kafka.consumer;
+
+import org.apache.eventmesh.api.AbstractContext;
+import org.apache.eventmesh.api.EventListener;
+import org.apache.eventmesh.api.consumer.Consumer;
+import org.apache.eventmesh.common.config.Config;
+import org.apache.eventmesh.storage.kafka.config.ClientConfiguration;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+
+import java.util.List;
+import java.util.Properties;
+
+import io.cloudevents.CloudEvent;
+
+@Config(field = "clientConfiguration")
+public class KafkaConsumerImpl implements Consumer {
+
+    private ConsumerImpl consumer;
+
+    /**
+     * Unified configuration class corresponding to kafka-client.properties
+     */
+    private ClientConfiguration clientConfiguration;
+
+    @Override
+    public synchronized void init(Properties props) throws Exception {
+        String namesrvAddr = clientConfiguration.getNamesrvAddr();
+        String consumerGroup = props.getProperty("consumerGroup");
+
+        // Other config props
+        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, namesrvAddr);
+        props.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup);
+        consumer = new ConsumerImpl(props);
+    }
+
+    @Override
+    public void updateOffset(List<CloudEvent> cloudEvents, AbstractContext 
context) {
+        consumer.updateOffset(cloudEvents, context);
+    }
+
+    @Override
+    public void subscribe(String topic) throws Exception {
+        consumer.subscribe(topic);
+    }
+
+    @Override
+    public boolean isStarted() {
+        return consumer.isStarted();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return consumer.isClosed();
+    }
+
+    @Override
+    public synchronized void start() {
+        consumer.start();
+    }
+
+    @Override
+    public void unsubscribe(String topic) {
+        consumer.unsubscribe(topic);
+    }
+
+    @Override
+    public void registerEventListener(EventListener listener) {
+        consumer.registerEventListener(listener);
+    }
+
+    @Override
+    public synchronized void shutdown() {
+        consumer.shutdown();
+    }
+
+    public ClientConfiguration getClientConfiguration() {
+        return this.clientConfiguration;
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerRunner.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerRunner.java
new file mode 100644
index 000000000..2ba0e6d34
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/consumer/KafkaConsumerRunner.java
@@ -0,0 +1,119 @@
+/*
+ * 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.storage.kafka.consumer;
+
+import org.apache.eventmesh.api.EventListener;
+import org.apache.eventmesh.api.EventMeshAction;
+import org.apache.eventmesh.api.EventMeshAsyncConsumeContext;
+
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.common.errors.WakeupException;
+
+import java.time.Duration;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+import io.cloudevents.CloudEvent;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class KafkaConsumerRunner implements Runnable {
+
+    private final AtomicBoolean closed = new AtomicBoolean(false);
+    private final KafkaConsumer<String, CloudEvent> consumer;
+    private ConcurrentHashMap<CloudEvent, Long> cloudEventToOffset;
+    private EventListener listener;
+    private AtomicInteger offset;
+
+    public KafkaConsumerRunner(KafkaConsumer<String, CloudEvent> 
kafkaConsumer) {
+        this.consumer = kafkaConsumer;
+        cloudEventToOffset = new ConcurrentHashMap<>();
+    }
+
+    public synchronized void setListener(EventListener listener) {
+        this.listener = listener;
+    }
+
+    public long getOffset(CloudEvent cloudEvent) {
+        return cloudEventToOffset.getOrDefault(cloudEvent, 0L);
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (!closed.get()) {
+                if (consumer.subscription().isEmpty()) {
+                    // consumer cannot poll if it is subscribe to nothing
+                    continue;
+                }
+                ConsumerRecords<String, CloudEvent> records = 
consumer.poll(Duration.ofMillis(10000));
+                // Handle new records
+                records.forEach(rec -> {
+                    try {
+                        CloudEvent cloudEvent = rec.value();
+                        String topicName = cloudEvent.getSubject();
+                        EventMeshAsyncConsumeContext 
eventMeshAsyncConsumeContext = new EventMeshAsyncConsumeContext() {
+                            @Override
+                            public void commit(EventMeshAction action) {
+                                switch (action) {
+                                    case CommitMessage:
+                                        // update offset
+                                        log.info("message commit, topic: {}, 
current offset:{}", topicName,
+                                            rec.offset());
+                                        break;
+                                    case ReconsumeLater:
+                                        // don't update offset
+                                        break;
+                                    case ManualAck:
+                                        // update offset
+                                        log.info("message ack, topic: {}, 
current offset:{}", topicName, rec.offset());
+                                        break;
+                                    default:
+                                }
+                            }
+                        };
+                        cloudEventToOffset.put(cloudEvent, rec.offset());
+                        if (listener != null) {
+                            listener.consume(cloudEvent, 
eventMeshAsyncConsumeContext);
+                        }
+                    } catch (Exception e) {
+                        log.info("Error parsing cloudevents: {}", 
e.getMessage());
+                    }
+                });
+            }
+        } catch (WakeupException e) {
+            // Ignore exception if closing
+            if (!closed.get()) {
+                throw e;
+            }
+        } finally {
+            consumer.close();
+        }
+    }
+
+    // Shutdown hook which can be called from a separate thread
+    public void shutdown() {
+        closed.set(true);
+        consumer.wakeup();
+    }
+}
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/KafkaProducerImpl.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/KafkaProducerImpl.java
new file mode 100644
index 000000000..fbc9352a8
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/KafkaProducerImpl.java
@@ -0,0 +1,104 @@
+/*
+ * 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.storage.kafka.producer;
+
+import org.apache.eventmesh.api.RequestReplyCallback;
+import org.apache.eventmesh.api.SendCallback;
+import org.apache.eventmesh.api.producer.Producer;
+import org.apache.eventmesh.common.config.Config;
+import org.apache.eventmesh.storage.kafka.config.ClientConfiguration;
+
+import org.apache.kafka.clients.producer.ProducerConfig;
+
+import java.util.Properties;
+
+import io.cloudevents.CloudEvent;
+
+@Config(field = "clientConfiguration")
+public class KafkaProducerImpl implements Producer {
+
+    private ProducerImpl producer;
+
+    /**
+     * Unified configuration class corresponding to kafka-client.properties
+     */
+    private ClientConfiguration clientConfiguration;
+
+    @Override
+    public synchronized void init(Properties keyValue) {
+        String namesrvAddr = clientConfiguration.getNamesrvAddr();
+
+        keyValue.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, namesrvAddr);
+        this.producer = new ProducerImpl(keyValue);
+    }
+
+    @Override
+    public boolean isStarted() {
+        return producer.isStarted();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return producer.isClosed();
+    }
+
+    @Override
+    public void start() {
+        producer.start();
+    }
+
+    @Override
+    public synchronized void shutdown() {
+        producer.shutdown();
+    }
+
+    @Override
+    public void publish(CloudEvent message, SendCallback sendCallback) throws 
Exception {
+        producer.sendAsync(message, sendCallback);
+    }
+
+    @Override
+    public void request(CloudEvent message, RequestReplyCallback rrCallback, 
long timeout) throws Exception {
+        producer.request(message, rrCallback, timeout);
+    }
+
+    @Override
+    public boolean reply(final CloudEvent message, final SendCallback 
sendCallback) throws Exception {
+        producer.reply(message, sendCallback);
+        return true;
+    }
+
+    @Override
+    public void checkTopicExist(String topic) throws Exception {
+        this.producer.checkTopicExist(topic);
+    }
+
+    @Override
+    public void setExtFields() {
+        // producer.setExtFields();
+    }
+
+    @Override
+    public void sendOneway(CloudEvent message) {
+        producer.sendOneway(message);
+    }
+
+    public ClientConfiguration getClientConfiguration() {
+        return this.clientConfiguration;
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/ProducerImpl.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/ProducerImpl.java
new file mode 100644
index 000000000..1d661dcc9
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/producer/ProducerImpl.java
@@ -0,0 +1,133 @@
+/*
+ * 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.storage.kafka.producer;
+
+import org.apache.eventmesh.api.RequestReplyCallback;
+import org.apache.eventmesh.api.SendCallback;
+import org.apache.eventmesh.api.SendResult;
+import org.apache.eventmesh.api.exception.OnExceptionContext;
+import org.apache.eventmesh.api.exception.StorageConnectorRuntimeException;
+
+
+import org.apache.kafka.clients.admin.Admin;
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.common.serialization.StringSerializer;
+
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.kafka.CloudEventSerializer;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@SuppressWarnings("deprecation")
+public class ProducerImpl {
+
+    private KafkaProducer<String, CloudEvent> producer;
+    Properties properties;
+
+    private AtomicBoolean isStarted;
+
+    public ProducerImpl(Properties props) {
+        this.isStarted = new AtomicBoolean(false);
+
+        properties = new Properties();
+        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
+            props.getProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG));
+        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 
StringSerializer.class);
+        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 
CloudEventSerializer.class);
+        this.producer = new KafkaProducer<>(properties);
+    }
+
+    public boolean isStarted() {
+        return isStarted.get();
+    }
+
+    public boolean isClosed() {
+        return !isStarted.get();
+    }
+
+    public void start() {
+        isStarted.compareAndSet(false, true);
+    }
+
+    public void shutdown() {
+        isStarted.compareAndSet(true, false);
+    }
+
+    public ProducerImpl init(Properties properties) throws Exception {
+        return new ProducerImpl(properties);
+    }
+
+    public void send(CloudEvent cloudEvent) {
+        try {
+            this.producer.send(new 
ProducerRecord<>(Objects.requireNonNull(cloudEvent.getSubject()), cloudEvent));
+        } catch (Exception e) {
+            log.error(String.format("Send message oneway Exception, %s", 
cloudEvent), e);
+        }
+    }
+
+    public void checkTopicExist(String topic) throws ExecutionException, 
InterruptedException, StorageConnectorRuntimeException {
+        Admin admin = Admin.create(properties);
+        Set<String> topicNames = admin.listTopics().names().get();
+        admin.close();
+        boolean exist = topicNames.contains(topic);
+        if (!exist) {
+            throw new StorageConnectorRuntimeException(String.format("topic:%s 
is not exist", topic));
+        }
+    }
+
+    public void request(CloudEvent cloudEvent, RequestReplyCallback 
rrCallback, long timeout) throws Exception {
+        throw new StorageConnectorRuntimeException("Request is not supported");
+    }
+
+    public boolean reply(CloudEvent cloudEvent, SendCallback sendCallback) 
throws Exception {
+        throw new StorageConnectorRuntimeException("Reply is not supported");
+    }
+
+    public void sendOneway(CloudEvent message) {
+    }
+
+    public void sendAsync(CloudEvent cloudEvent, SendCallback sendCallback) {
+        try {
+            this.producer.send(new ProducerRecord<>(cloudEvent.getSubject(), 
cloudEvent), (metadata, exception) -> {
+                if (exception != null) {
+                    StorageConnectorRuntimeException onsEx = new 
StorageConnectorRuntimeException(exception.getMessage(), exception);
+                    OnExceptionContext context = new OnExceptionContext();
+                    context.setTopic(cloudEvent.getSubject());
+                    context.setException(onsEx);
+                    sendCallback.onException(context);
+                } else {
+                    SendResult sendResult = new SendResult();
+                    sendResult.setTopic(cloudEvent.getSubject());
+                    sendResult.setMessageId(cloudEvent.getId());
+                    sendCallback.onSuccess(sendResult);
+                }
+            });
+        } catch (Exception e) {
+            log.error(String.format("Send message oneway Exception, %s", 
cloudEvent), e);
+        }
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/storage/StorageResourceServiceKafkaImpl.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/storage/StorageResourceServiceKafkaImpl.java
new file mode 100644
index 000000000..5ee4b8eb8
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/java/org/apache/eventmesh/storage/kafka/storage/StorageResourceServiceKafkaImpl.java
@@ -0,0 +1,33 @@
+/*
+ * 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.storage.kafka.storage;
+
+import org.apache.eventmesh.api.storage.StorageResourceService;
+
+public class StorageResourceServiceKafkaImpl implements StorageResourceService 
{
+
+    @Override
+    public void init() throws Exception {
+
+    }
+
+    @Override
+    public void release() throws Exception {
+
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.consumer.Consumer
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.consumer.Consumer
new file mode 100644
index 000000000..015cc5c7a
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.consumer.Consumer
@@ -0,0 +1,16 @@
+# 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.
+
+kafka=org.apache.eventmesh.storage.kafka.consumer.KafkaConsumerImpl
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.producer.Producer
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.producer.Producer
new file mode 100644
index 000000000..15ad33471
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.producer.Producer
@@ -0,0 +1,16 @@
+# 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.
+
+kafka=org.apache.eventmesh.storage.kafka.producer.KafkaProducerImpl
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.storage.StorageResourceService
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.storage.StorageResourceService
new file mode 100644
index 000000000..6b314f5c5
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.storage.StorageResourceService
@@ -0,0 +1,16 @@
+# 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.
+
+kafka=org.apache.eventmesh.storage.kafka.storage.StorageResourceServiceKafkaImpl
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/kafka-client.properties
 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/kafka-client.properties
new file mode 100644
index 000000000..31923d73d
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/main/resources/kafka-client.properties
@@ -0,0 +1,135 @@
+#
+# 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.
+#
+#######################kafka-client##################
+eventMesh.server.kafka.namesrvAddr=127.0.0.1:9092;127.0.0.1:9092
+eventMesh.server.kafka.cluster=DefaultCluster
+eventMesh.server.kafka.accessKey=********
+eventMesh.server.kafka.secretKey=********
+
+# see kafka.server.KafkaConfig for additional details and defaults
+
+############################# Server Basics #############################
+
+# The id of the broker. This must be set to a unique integer for each broker.
+broker.id=0
+
+############################# Socket Server Settings 
#############################
+
+# The port the socket server listens on
+eventMesh.server.kafka.port=9092
+eventMesh.server.kafka.bootstrap.servers=localhost:9092
+
+# Hostname the broker will bind to. If not set, the server will bind to all 
interfaces
+#host.name=localhost
+
+# Hostname the broker will advertise to producers and consumers. If not set, 
it uses the
+# value for "host.name" if configured.  Otherwise, it will use the value 
returned from
+# java.net.InetAddress.getCanonicalHostName().
+#advertised.host.name=<hostname routable by clients>
+
+# The port to publish to ZooKeeper for clients to use. If this is not set,
+# it will publish the same port that the broker binds to.
+#advertised.port=<port accessible by clients>
+
+# The number of threads handling network requests
+num.network.threads=2
+
+# The number of threads doing disk I/O
+num.io.threads=8
+
+# The send buffer (SO_SNDBUF) used by the socket server
+socket.send.buffer.bytes=1048576
+
+# The receive buffer (SO_RCVBUF) used by the socket server
+socket.receive.buffer.bytes=1048576
+
+# The maximum size of a request that the socket server will accept (protection 
against OOM)
+socket.request.max.bytes=104857600
+
+
+############################# Log Basics #############################
+
+# A comma seperated list of directories under which to store log files
+log.dirs=c:/tmp/kafka-logs/broker0
+
+# The default number of log partitions per topic. More partitions allow greater
+# parallelism for consumption, but this will also result in more files across
+# the brokers.
+num.partitions=2
+
+############################# Log Flush Policy #############################
+
+# Messages are immediately written to the filesystem but by default we only 
fsync() to sync
+# the OS cache lazily. The following configurations control the flush of data 
to disk.
+# There are a few important trade-offs here:
+#    1. Durability: Unflushed data may be lost if you are not using 
replication.
+#    2. Latency: Very large flush intervals may lead to latency spikes when 
the flush does occur as there will be a lot of data to flush.
+#    3. Throughput: The flush is generally the most expensive operation, and a 
small flush interval may lead to exceessive seeks.
+# The settings below allow one to configure the flush policy to flush data 
after a period of time or
+# every N messages (or both). This can be done globally and overridden on a 
per-topic basis.
+
+# The number of messages to accept before forcing a flush of data to disk
+#log.flush.interval.messages=10000
+
+# The maximum amount of time a message can sit in a log before we force a flush
+#log.flush.interval.ms=1000
+
+############################# Log Retention Policy 
#############################
+
+# The following configurations control the disposal of log segments. The 
policy can
+# be set to delete segments after a period of time, or after a given size has 
accumulated.
+# A segment will be deleted whenever *either* of these criteria are met. 
Deletion always happens
+# from the end of the log.
+
+# The minimum age of a log file to be eligible for deletion
+log.retention.hours=168
+
+# A size-based retention policy for logs. Segments are pruned from the log as 
long as the remaining
+# segments don't drop below log.retention.bytes.
+#log.retention.bytes=1073741824
+
+# The maximum size of a log segment file. When this size is reached a new log 
segment will be created.
+log.segment.bytes=536870912
+
+# The interval at which log segments are checked to see if they can be deleted 
according
+# to the retention policies
+log.retention.check.interval.ms=60000
+
+# By default the log cleaner is disabled and the log retention policy will 
default to just delete segments after their retention expires.
+# If log.cleaner.enable=true is set the cleaner will be enabled and individual 
logs can then be marked for log compaction.
+log.cleaner.enable=false
+
+############################# Zookeeper #############################
+
+# Zookeeper connection string (see zookeeper docs for details).
+# This is a comma separated host:port pairs, each corresponding to a zk
+# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
+# You can also append an optional chroot string to the urls to specify the
+# root directory for all kafka znodes.
+zookeeper.connect=localhost:2181
+
+# Timeout in ms for connecting to zookeeper
+zookeeper.connection.timeout.ms=1000000
+
+############################# Producer Basics #############################
+
+# list of brokers used for bootstrapping knowledge about the rest of the 
cluster
+# format: host1:port1,host2:port2 ...
+metadata.broker.list=localhost:9092
+
+# specifies whether the messages are sent asynchronously (async) or 
synchronously (sync)
+producer.type=async
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/test/java/org/apache/eventmesh/storage/kafka/config/ClientConfigurationTest.java
 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/java/org/apache/eventmesh/storage/kafka/config/ClientConfigurationTest.java
new file mode 100644
index 000000000..9b5e494ce
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/java/org/apache/eventmesh/storage/kafka/config/ClientConfigurationTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.storage.kafka.config;
+
+import org.apache.eventmesh.api.factory.StoragePluginFactory;
+import org.apache.eventmesh.storage.kafka.consumer.KafkaConsumerImpl;
+import org.apache.eventmesh.storage.kafka.producer.KafkaProducerImpl;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * test config of Kafka SPI Impl
+ */
+public class ClientConfigurationTest {
+
+    /**
+     * test KafkaConsumerImpl config init when ConnectorPluginFactory get it
+     */
+    @Test
+    public void getConfigWhenKafkaConsumerImplInit() {
+        KafkaConsumerImpl consumer = (KafkaConsumerImpl) 
StoragePluginFactory.getMeshMQPushConsumer("kafka");
+        ClientConfiguration config = consumer.getClientConfiguration();
+        assertConfig(config);
+    }
+
+    /**
+     * test KafkaProducerImpl config init when ConnectorPluginFactory get it
+     */
+    @Test
+    public void getConfigWhenKafkaProducerImplInit() {
+        KafkaProducerImpl producer = (KafkaProducerImpl) 
StoragePluginFactory.getMeshMQProducer("kafka");
+        ClientConfiguration config = producer.getClientConfiguration();
+        assertConfig(config);
+    }
+
+    private void assertConfig(ClientConfiguration config) {
+        Assert.assertEquals(config.getNamesrvAddr(), 
"127.0.0.1:9092;127.0.0.1:9092");
+        Assert.assertEquals(config.getClientUserName(), "username-succeed!!!");
+        Assert.assertEquals(config.getClientPass(), "password-succeed!!!");
+        Assert.assertEquals(config.getConsumeThreadMin(), 
Integer.valueOf(1816));
+        Assert.assertEquals(config.getConsumeThreadMax(), 
Integer.valueOf(2816));
+        Assert.assertEquals(config.getConsumeQueueSize(), 
Integer.valueOf(3816));
+        Assert.assertEquals(config.getPullBatchSize(), Integer.valueOf(4816));
+        Assert.assertEquals(config.getAckWindow(), Integer.valueOf(5816));
+        Assert.assertEquals(config.getPubWindow(), Integer.valueOf(6816));
+        Assert.assertEquals(config.getConsumeTimeout(), 7816);
+        Assert.assertEquals(config.getPollNameServerInterval(), 
Integer.valueOf(8816));
+        Assert.assertEquals(config.getHeartbeatBrokerInterval(), 
Integer.valueOf(9816));
+        Assert.assertEquals(config.getRebalanceInterval(), 
Integer.valueOf(11816));
+        Assert.assertEquals(config.getClusterName(), "cluster-succeed!!!");
+        Assert.assertEquals(config.getAccessKey(), "accessKey-succeed!!!");
+        Assert.assertEquals(config.getSecretKey(), "secretKey-succeed!!!");
+    }
+}
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/META-INF/services/org.apache.io.openmessaging.producer.Producer
 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/META-INF/services/org.apache.io.openmessaging.producer.Producer
new file mode 100644
index 000000000..358b84da4
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/META-INF/services/org.apache.io.openmessaging.producer.Producer
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+org.apache.eventmesh.connector.kafka.producer.ProducerImpl
\ No newline at end of file
diff --git 
a/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/kafka-client.properties
 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/kafka-client.properties
new file mode 100644
index 000000000..3b0c217e0
--- /dev/null
+++ 
b/eventmesh-storage/eventmesh-storage-kafka/src/test/resources/kafka-client.properties
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+#######################kafka-client##################
+eventMesh.server.kafka.namesrvAddr=127.0.0.1:9092;127.0.0.1:9092
+eventMesh.server.kafka.username=username-succeed!!!
+eventMesh.server.kafka.password=password-succeed!!!
+eventMesh.server.kafka.client.consumeThreadMin=1816
+eventMesh.server.kafka.client.consumeThreadMax=2816
+eventMesh.server.kafka.client.consumeThreadPoolQueueSize=3816
+eventMesh.server.kafka.client.pullBatchSize=4816
+eventMesh.server.kafka.client.ackwindow=5816
+eventMesh.server.kafka.client.pubwindow=6816
+eventMesh.server.kafka.client.comsumeTimeoutInMin=7816
+eventMesh.server.kafka.client.pollNameServerInterval=8816
+eventMesh.server.kafka.client.heartbeatBrokerInterval=9816
+eventMesh.server.kafka.client.rebalanceInterval=11816
+eventMesh.server.kafka.cluster=cluster-succeed!!!
+eventMesh.server.kafka.accessKey=accessKey-succeed!!!
+eventMesh.server.kafka.secretKey=secretKey-succeed!!!
+eventMesh.server.kafka.username=username-succeed!!!
+eventMesh.server.kafka.password=password-succeed!!!
+eventMesh.server.kafka.client.consumeThreadMin=1816
+eventMesh.server.kafka.client.consumeThreadMax=2816
+eventMesh.server.kafka.client.consumeThreadPoolQueueSize=3816
+eventMesh.server.kafka.client.pullBatchSize=4816
+eventMesh.server.kafka.client.ackwindow=5816
+eventMesh.server.kafka.client.pubwindow=6816
+eventMesh.server.kafka.client.comsumeTimeoutInMin=7816
+eventMesh.server.kafka.client.pollNameServerInterval=8816
+eventMesh.server.kafka.client.heartbeatBrokerInterval=9816
+eventMesh.server.kafka.client.rebalanceInterval=11816
+eventMesh.server.kafka.cluster=cluster-succeed!!!
+eventMesh.server.kafka.accessKey=accessKey-succeed!!!
+eventMesh.server.kafka.secretKey=secretKey-succeed!!!
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 28cd917b8..8456e9430 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -25,6 +25,8 @@ include 'eventmesh-examples'
 include 'eventmesh-spi'
 include 'eventmesh-storage:eventmesh-storage-api'
 include 'eventmesh-storage:eventmesh-storage-standalone'
+include 'eventmesh-storage:eventmesh-storage-kafka'
+include 'eventmesh-storage:eventmesh-storage-redis'
 include 'eventmesh-storage:eventmesh-storage-rocketmq'
 include 'eventmesh-connector-plugin:eventmesh-connector-api'
 include 'eventmesh-connector-plugin:eventmesh-connector-knative'


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to