You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@seatunnel.apache.org by ga...@apache.org on 2023/05/15 06:37:46 UTC

[incubator-seatunnel-web] branch add_canvas_job_define updated: add datasource module (#54)

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

gaojun2048 pushed a commit to branch add_canvas_job_define
in repository https://gitbox.apache.org/repos/asf/incubator-seatunnel-web.git


The following commit(s) were added to refs/heads/add_canvas_job_define by this push:
     new 4398dc88 add datasource module (#54)
4398dc88 is described below

commit 4398dc88c2a2f6ed957ef3a9f400c7a66a616a43
Author: Eric <ga...@gmail.com>
AuthorDate: Mon May 15 14:37:41 2023 +0800

    add datasource module (#54)
---
 README.md                                          |   2 +-
 build.sh                                           |   2 +-
 pom.xml                                            | 100 ++-
 seatunnel-datasource/pom.xml                       |  40 ++
 .../seatunnel-datasource-client/pom.xml            |  71 ++
 .../datasource/AbstractDataSourceClient.java       | 135 ++++
 .../seatunnel/datasource/DataSourceClient.java     |  28 +
 .../datasource/annotation/ThreadSafe.java          |  29 +
 .../exception/DataSourceSDKException.java          |  37 +
 .../datasource/request/PageQueryRequest.java       |  42 ++
 .../request/VirtualTableCreateRequest.java         |  40 ++
 .../request/VirtualTableFieldRequest.java          |  42 ++
 .../request/VirtualTableUpdateRequest.java         |  36 +
 .../datasource/response/BaseResponse.java          |  38 ++
 .../response/DataSourceDetailResponse.java         |  38 ++
 .../datasource/response/PageDataResponse.java      |  32 +
 .../datasource/response/PageResultResponse.java    |  36 +
 .../response/VirtualTableDetailResponse.java       |  42 ++
 .../response/VirtualTableFieldResponse.java        |  42 ++
 .../datasource/response/VirtualTableResponse.java  |  36 +
 .../datasource/service/DataSourceService.java      | 116 ++++
 .../datasource/s3/S3DatasourceChannelTest.java     |  55 ++
 .../seatunnel/datasource/DataSourceClientTest.java |  48 ++
 .../datasource-all/pom.xml                         | 112 +++
 .../datasource-elasticsearch/pom.xml               |  62 ++
 .../ElasticSearchDataSourceChannel.java            | 135 ++++
 .../ElasticSearchDataSourceConfig.java             |  36 +
 .../ElasticSearchDataSourceFactory.java            |  48 ++
 .../elasticsearch/ElasticSearchOptionRule.java     | 109 +++
 .../client/ElasticsearchClusterInfo.java           |  30 +
 .../plugin/elasticsearch/client/EsRestClient.java  | 371 ++++++++++
 .../elasticsearch/client/ResponseException.java    |  45 ++
 .../plugin/elasticsearch/client/SSLUtils.java      | 165 +++++
 .../ElasticSearchDataSourceChannelTest.java        | 114 ++++
 .../ElasticSearchDataSourceFactoryTest.java        |  43 ++
 .../datasource-jdbc-clickhouse/pom.xml             |  59 ++
 .../jdbc/ClickhouseDataSourceConfig.java           |  59 ++
 .../jdbc/ClickhouseJdbcDataSourceChannel.java      | 177 +++++
 .../jdbc/ClickhouseJdbcDataSourceFactory.java      |  48 ++
 .../clickhouse/jdbc/ClickhouseOptionRule.java      |  68 ++
 .../datasource-jdbc-hive/pom.xml                   |  35 +
 .../plugin/hive/jdbc/HiveJdbcConstants.java        |  29 +
 .../hive/jdbc/HiveJdbcDataSourceChannel.java       | 215 ++++++
 .../hive/jdbc/HiveJdbcDataSourceFactory.java       |  53 ++
 .../plugin/hive/jdbc/HiveJdbcOptionRule.java       |  42 ++
 .../datasource-jdbc-mysql/pom.xml                  |  59 ++
 .../plugin/mysql/jdbc/MysqlDataSourceConfig.java   |  51 ++
 .../mysql/jdbc/MysqlJdbcDataSourceChannel.java     | 182 +++++
 .../mysql/jdbc/MysqlJdbcDataSourceFactory.java     |  48 ++
 .../plugin/mysql/jdbc/MysqlOptionRule.java         |  69 ++
 .../datasource-jdbc-oracle/pom.xml                 |  59 ++
 .../oracle/jdbc/OracleDataSourceChannel.java       | 174 +++++
 .../plugin/oracle/jdbc/OracleDataSourceConfig.java |  53 ++
 .../oracle/jdbc/OracleJdbcDataSourceFactory.java   |  47 ++
 .../plugin/oracle/jdbc/OracleOptionRule.java       |  67 ++
 .../datasource-jdbc-postgresql/pom.xml             |  58 ++
 .../jdbc/PostgresqlDataSourceChannel.java          | 184 +++++
 .../jdbc/PostgresqlDataSourceConfig.java           |  62 ++
 .../jdbc/PostgresqlDataSourceFactory.java          |  46 ++
 .../postgresql/jdbc/PostgresqlOptionRule.java      |  69 ++
 .../datasource-jdbc-redshift/pom.xml               |  59 ++
 .../redshift/jdbc/RedshiftDataSourceChannel.java   | 180 +++++
 .../redshift/jdbc/RedshiftDataSourceConfig.java    |  62 ++
 .../redshift/jdbc/RedshiftDataSourceFactory.java   |  48 ++
 .../plugin/redshift/jdbc/RedshiftOptionRule.java   |  69 ++
 .../datasource-jdbc-sqlserver/pom.xml              |  59 ++
 .../sqlserver/jdbc/SqlServerDataSourceChannel.java | 175 +++++
 .../sqlserver/jdbc/SqlServerDataSourceConfig.java  |  60 ++
 .../sqlserver/jdbc/SqlServerDataSourceFactory.java |  48 ++
 .../plugin/sqlserver/jdbc/SqlServerOptionRule.java |  68 ++
 .../datasource-jdbc-starrocks/pom.xml              |  59 ++
 .../starrocks/jdbc/StarRocksDataSourceConfig.java  |  53 ++
 .../jdbc/StarRocksJdbcDataSourceChannel.java       | 175 +++++
 .../jdbc/StarRocksJdbcDataSourceFactory.java       |  46 ++
 .../plugin/starrocks/jdbc/StarRocksOptionRule.java |  69 ++
 .../datasource-jdbc-tidb/pom.xml                   |  36 +
 .../plugin/tidb/jdbc/TidbDataSourceConfig.java     |  51 ++
 .../tidb/jdbc/TidbJdbcDataSourceChannel.java       | 182 +++++
 .../tidb/jdbc/TidbJdbcDataSourceFactory.java       |  48 ++
 .../plugin/tidb/jdbc/TidbOptionRule.java           |  69 ++
 .../datasource-kafka/pom.xml                       |  56 ++
 .../plugin/kafka/KafkaDataSourceChannel.java       | 115 ++++
 .../plugin/kafka/KafkaDataSourceFactory.java       |  58 ++
 .../datasource/plugin/kafka/KafkaOptionRule.java   |  67 ++
 .../plugin/kafka/KafkaRequestParamsUtils.java      |  55 ++
 .../plugin/kafka/KafkaDataSourceChannelTest.java   | 106 +++
 .../plugin/kafka/KafkaRequestParamsUtilsTest.java  |  66 ++
 .../datasource-mysql-cdc/pom.xml                   |  57 ++
 .../cdc/mysql/MysqlCDCDataSourceChannel.java       | 249 +++++++
 .../plugin/cdc/mysql/MysqlCDCDataSourceConfig.java |  34 +
 .../cdc/mysql/MysqlCDCDataSourceFactory.java       |  46 ++
 .../plugin/cdc/mysql/MysqlCDCOptionRule.java       |  74 ++
 .../datasource-plugins-api/pom.xml                 |  37 +
 .../datasource/plugin/api/DataSourceChannel.java   |  83 +++
 .../datasource/plugin/api/DataSourceFactory.java   |  29 +
 .../plugin/api/DataSourcePluginException.java      |  37 +
 .../plugin/api/DataSourcePluginInfo.java           |  53 ++
 .../plugin/api/DatasourcePluginTypeEnum.java       |  53 ++
 .../plugin/api/common/ParamtersUtils.java          |  40 ++
 .../datasource/plugin/api/model/TableField.java    |  40 ++
 .../datasource/plugin/api/utils/JdbcUtils.java     |  43 ++
 .../plugin/api/common/ParamtersUtilsTest.java      |  49 ++
 .../datasource-s3-redshift/pom.xml                 |  67 ++
 .../plugin/redshift/s3/HadoopS3AConfiguration.java | 100 +++
 .../redshift/s3/S3RedshiftDataSourceChannel.java   | 268 ++++++++
 .../redshift/s3/S3RedshiftDataSourceFactory.java   |  56 ++
 .../plugin/redshift/s3/S3RedshiftOptionRule.java   | 191 ++++++
 .../datasource-s3/pom.xml                          |  68 ++
 .../plugin/s3/HadoopS3AConfiguration.java          |  95 +++
 .../datasource/plugin/s3/S3DataSourceFactory.java  |  58 ++
 .../datasource/plugin/s3/S3DatasourceChannel.java  |  89 +++
 .../datasource/plugin/s3/S3OptionRule.java         | 166 +++++
 .../datasource-sqlserver-cdc/pom.xml               |  52 ++
 .../sqlserver/SqlServerCDCDataSourceChannel.java   | 231 +++++++
 .../sqlserver/SqlServerCDCDataSourceConfig.java    |  34 +
 .../sqlserver/SqlServerCDCDataSourceFactory.java   |  47 ++
 .../cdc/sqlserver/SqlServerCDCOptionRule.java      |  73 ++
 .../test/TestSqlServerCDCDataSourceChannel.java    |  48 ++
 .../datasource-starrocks/pom.xml                   |  57 ++
 .../plugin/starrocks/StarRocksCatalog.java         | 224 ++++++
 .../starrocks/StarRocksDataSourceChannel.java      | 147 ++++
 .../starrocks/StarRocksDataSourceConfig.java       |  34 +
 .../starrocks/StarRocksDataSourceFactory.java      |  45 ++
 .../plugin/starrocks/StarRocksOptionRule.java      |  74 ++
 .../seatunnel-datasource-plugins/pom.xml           |  53 ++
 seatunnel-server/pom.xml                           |   5 +
 seatunnel-server/seatunnel-app/pom.xml             | 154 +----
 .../src/main/assembly/seatunnel-web.xml            |  64 --
 .../src/main/bin/seatunnel-backend-daemon.sh       |   2 +-
 .../seatunnel/app/controller/AuthController.java   |   2 +-
 .../src/main/resources/application.yml             |   2 +-
 seatunnel-web-dist/pom.xml                         | 349 +++++++++-
 .../release-docs/{binary => }/LICENSE              |  17 +-
 .../release-docs/{binary => }/NOTICE               |   0
 .../{binary => }/licenses/LICENSE-Jetty-9.x.txt    |   0
 .../{binary => }/licenses/LICENSE-aspectj.txt      |   0
 .../LICENSE-cron-utils.txt}                        |  24 +-
 .../{binary => }/licenses/LICENSE-h2.txt           |   0
 .../{binary => }/licenses/LICENSE-jakarta.txt      |   0
 .../release-docs/licenses/LICENSE-javassist.html   | 373 ++++++++++
 .../licenses/LICENSE-javax-annotation.txt          |   0
 .../release-docs/licenses/LICENSE-javax.el.txt     | 759 +++++++++++++++++++++
 .../{binary => }/licenses/LICENSE-jsoup.txt        |   0
 .../{binary => }/licenses/LICENSE-logback.txt      |   0
 .../{binary => }/licenses/LICENSE-mapstruct.txt    |   0
 .../{binary => }/licenses/LICENSE-slf4j.txt        |   0
 .../licenses/LICENSE-spring-plugin.txt             |   0
 .../{binary => }/licenses/LICENSE-swagger-core.txt |   0
 .../src/main/assembly/seatunnel-web-ci.xml         |  57 +-
 .../src/main/assembly/seatunnel-web.xml            |  48 +-
 tools/dependencies/checkLicense.sh                 |   2 +-
 tools/dependencies/known-dependencies.txt          |   3 +-
 152 files changed, 11316 insertions(+), 255 deletions(-)

diff --git a/README.md b/README.md
index 822472d8..4800fcff 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ cd incubator-seatunnel-web
 sh build.sh code
 ```
 
-Then you can find the installer package in dir `incubator-seatunnel-web/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-${project.version}.tar.gz`.
+Then you can find the installer package in dir `incubator-seatunnel-web/seatunnel-web-dist/target/apache-seatunnel-web-incubating-${project.version}.tar.gz`.
 
 #### 3.2 Install
 
diff --git a/build.sh b/build.sh
index 6ce3ed7f..9a849703 100644
--- a/build.sh
+++ b/build.sh
@@ -29,7 +29,7 @@ DOCKER_VERSION=1.0.0-snapshot
 code() {
   /bin/sh $WORKDIR/mvnw clean package -DskipTests
   # mv release zip
-  mv $WORKDIR/seatunnel-server/seatunnel-app/target/apache-seatunnel-web-incubating-1.0.0-SNAPSHOT.zip $WORKDIR/
+  mv $WORKDIR/seatunnel-web-dist/target/apache-seatunnel-web-incubating-1.0.0-SNAPSHOT.zip $WORKDIR/
 }
 
 # build image
diff --git a/pom.xml b/pom.xml
index 6097e7c8..b2a4da10 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,8 @@
 
     <modules>
 	    <module>seatunnel-server</module>
+	    <module>seatunnel-datasource</module>
+	    <module>seatunnel-web-dist</module>
     </modules>
 
     <properties>
@@ -97,6 +99,7 @@
         <maven-scm-provider-jgit.version>1.9.5</maven-scm-provider-jgit.version>
         <exec-maven-plugin.version>3.1.0</exec-maven-plugin.version>
         <checkstyle.fails.on.error>true</checkstyle.fails.on.error>
+        <maven-shade-plugin.version>3.3.0</maven-shade-plugin.version>
         <lombok.version>1.18.0</lombok.version>
         <skipUT>false</skipUT>
 
@@ -108,12 +111,20 @@
         <h2.version>2.1.214</h2.version>
         <junit.version>5.9.0</junit.version>
         <commons-collections4.version>4.4</commons-collections4.version>
-        <commons-lang3.version>3.4</commons-lang3.version>
+        <commons-lang3.version>3.12.0</commons-lang3.version>
         <guava.version>19.0</guava.version>
         <checker.qual.version>3.10.0</checker.qual.version>
         <log4j-core.version>2.17.1</log4j-core.version>
         <awaitility.version>4.2.0</awaitility.version>
         <seatunnel-framework.version>2.3.1</seatunnel-framework.version>
+        <redshift.version>2.1.0.9</redshift.version>
+        <hadoop-aws.version>3.1.4</hadoop-aws.version>
+        <aws-java-sdk-bundle.version>1.11.271</aws-java-sdk-bundle.version>
+        <auto-service.version>1.0.1</auto-service.version>
+        <lombok.version>1.18.24</lombok.version>
+        <e2e.dependency.skip>true</e2e.dependency.skip>
+        <maven-dependency-plugin.version>3.1.1</maven-dependency-plugin.version>
+        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
     </properties>
 
     <dependencyManagement>
@@ -237,6 +248,13 @@
                 <version>${seatunnel-framework.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.version}</version>
+                <scope>provided</scope>
+            </dependency>
+
             <!-- seatunnel connector test -->
             <dependency>
                 <groupId>org.apache.seatunnel</groupId>
@@ -402,6 +420,53 @@
                     <version>${maven-assembly-plugin.version}</version>
                 </plugin>
 
+                <!-- shade -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-shade-plugin</artifactId>
+                    <version>${maven-shade-plugin.version}</version>
+                    <configuration>
+                        <shadedArtifactAttached>false</shadedArtifactAttached>
+                        <createDependencyReducedPom>true</createDependencyReducedPom>
+                        <!-- Make sure the transitive dependencies are written to the generated pom under <dependencies> -->
+                        <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+                        <artifactSet>
+                            <excludes>
+                                <exclude>org.slf4j:*</exclude>
+                                <exclude>ch.qos.logback:*</exclude>
+                                <exclude>log4j:*</exclude>
+                                <exclude>org.apache.logging.log4j:*</exclude>
+                                <exclude>commons-logging:*</exclude>
+                            </excludes>
+                        </artifactSet>
+                        <filters>
+                            <filter>
+                                <artifact>*:*</artifact>
+                                <excludes>
+                                    <exclude>META-INF/*.SF</exclude>
+                                    <exclude>META-INF/*.DSA</exclude>
+                                    <exclude>META-INF/*.RSA</exclude>
+                                </excludes>
+                            </filter>
+                        </filters>
+                    </configuration>
+
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>shade</goal>
+                            </goals>
+                            <phase>package</phase>
+                            <configuration>
+                                <transformers combine.children="append">
+                                    <!-- The service transformer is needed to merge META-INF/services files -->
+                                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+                                </transformers>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+
                 <!-- checkstyle (Start) -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -494,7 +559,7 @@
                     <artifactId>license-maven-plugin</artifactId>
                     <version>${maven-license-maven-plugin}</version>
                     <configuration>
-                        <outputDirectory>${project.basedir}/seatunnel-dist/target/</outputDirectory>
+                        <outputDirectory>${project.basedir}/seatunnel-web-dist/target/</outputDirectory>
                         <thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
                         <sortArtifactByName>false</sortArtifactByName>
                         <useMissingFile>false</useMissingFile>
@@ -505,6 +570,37 @@
                         <excludedScopes>test,provided</excludedScopes>
                     </configuration>
                 </plugin>
+                <!-- make sure that flatten runs after shaded -->
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>flatten-maven-plugin</artifactId>
+                    <version>${flatten-maven-plugin.version}</version>
+                    <configuration>
+                        <updatePomFile>true</updatePomFile>
+                        <flattenMode>resolveCiFriendliesOnly</flattenMode>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <id>flatten</id>
+                            <goals>
+                                <goal>flatten</goal>
+                            </goals>
+                            <phase>process-resources</phase>
+                        </execution>
+                        <execution>
+                            <id>flatten.clean</id>
+                            <goals>
+                                <goal>clean</goal>
+                            </goals>
+                            <phase>clean</phase>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-dependency-plugin</artifactId>
+                    <version>${maven-dependency-plugin.version}</version>
+                </plugin>
             </plugins>
         </pluginManagement>
 
diff --git a/seatunnel-datasource/pom.xml b/seatunnel-datasource/pom.xml
new file mode 100644
index 00000000..a6fcbc7a
--- /dev/null
+++ b/seatunnel-datasource/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-web</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>seatunnel-datasource</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>seatunnel-datasource-client</module>
+        <module>seatunnel-datasource-plugins</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-client/pom.xml b/seatunnel-datasource/seatunnel-datasource-client/pom.xml
new file mode 100644
index 00000000..e697c677
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>seatunnel-datasource-client</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-all</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <configuration>
+                    <skip>${e2e.dependency.skip}</skip>
+                    <appendOutput>true</appendOutput>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java
new file mode 100644
index 00000000..7cc2783e
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/AbstractDataSourceClient.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.exception.DataSourceSDKException;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.service.DataSourceService;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class AbstractDataSourceClient implements DataSourceService {
+
+    private Map<String, DataSourcePluginInfo> supportedDataSourceInfo = new HashMap<>();
+
+    private Map<String, Integer> supportedDataSourceIndex = new HashMap<>();
+
+    protected List<DataSourcePluginInfo> supportedDataSources = new ArrayList<>();
+
+    private List<DataSourceChannel> dataSourceChannels = new ArrayList<>();
+
+    protected AbstractDataSourceClient() {
+        AtomicInteger dataSourceIndex = new AtomicInteger();
+        ServiceLoader.load(DataSourceFactory.class)
+            .forEach(
+                seaTunnelDataSourceFactory -> {
+                    seaTunnelDataSourceFactory
+                        .supportedDataSources()
+                        .forEach(
+                            dataSourceInfo -> {
+                                supportedDataSourceInfo.put(
+                                    dataSourceInfo.getName().toUpperCase(),
+                                    dataSourceInfo);
+                                supportedDataSourceIndex.put(
+                                    dataSourceInfo.getName().toUpperCase(),
+                                    dataSourceIndex.get());
+                                supportedDataSources.add(dataSourceInfo);
+                            });
+                    dataSourceChannels.add(seaTunnelDataSourceFactory.createChannel());
+                    dataSourceIndex.getAndIncrement();
+                });
+        if (supportedDataSourceInfo.isEmpty()) {
+            throw new DataSourceSDKException("No supported data source found");
+        }
+    }
+
+    @Override
+    public Boolean checkDataSourceConnectivity(
+        String pluginName, Map<String, String> dataSourceParams) {
+        return getDataSourceChannel(pluginName)
+            .checkDataSourceConnectivity(pluginName, dataSourceParams);
+    }
+
+    @Override
+    public List<DataSourcePluginInfo> listAllDataSources() {
+        return supportedDataSources;
+    }
+
+    protected DataSourceChannel getDataSourceChannel(String pluginName) {
+        checkNotNull(pluginName, "pluginName cannot be null");
+        Integer index = supportedDataSourceIndex.get(pluginName.toUpperCase());
+        if (index == null) {
+            throw new DataSourceSDKException(
+                "The %s plugin is not supported or plugin not exist.", pluginName);
+        }
+        return dataSourceChannels.get(index);
+    }
+
+    @Override
+    public OptionRule queryDataSourceFieldByName(String pluginName) {
+        return getDataSourceChannel(pluginName).getDataSourceOptions(pluginName);
+    }
+
+    @Override
+    public OptionRule queryMetadataFieldByName(String pluginName) {
+        return getDataSourceChannel(pluginName)
+            .getDatasourceMetadataFieldsByDataSourceName(pluginName);
+    }
+
+    @Override
+    public List<String> getTables(
+        String pluginName, String databaseName, Map<String, String> requestParams) {
+        return getDataSourceChannel(pluginName).getTables(pluginName, requestParams, databaseName);
+    }
+
+    @Override
+    public List<String> getDatabases(String pluginName, Map<String, String> requestParams) {
+        return getDataSourceChannel(pluginName).getDatabases(pluginName, requestParams);
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String databaseName,
+        String tableName) {
+        return getDataSourceChannel(pluginName)
+            .getTableFields(pluginName, requestParams, databaseName, tableName);
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String databaseName,
+        List<String> tableNames) {
+        return getDataSourceChannel(pluginName)
+            .getTableFields(pluginName, requestParams, databaseName, tableNames);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java
new file mode 100644
index 00000000..4bf46baa
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/DataSourceClient.java
@@ -0,0 +1,28 @@
+/*
+ * 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.seatunnel.datasource;
+
+import org.apache.seatunnel.datasource.annotation.ThreadSafe;
+
+@ThreadSafe
+public class DataSourceClient extends AbstractDataSourceClient {
+
+    public DataSourceClient() {
+        super();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java
new file mode 100644
index 00000000..55794b81
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java
@@ -0,0 +1,29 @@
+/*
+ * 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.seatunnel.datasource.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface ThreadSafe {}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java
new file mode 100644
index 00000000..3f90eabc
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/exception/DataSourceSDKException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.seatunnel.datasource.exception;
+
+public class DataSourceSDKException extends RuntimeException {
+
+    public DataSourceSDKException(String message) {
+        super(message);
+    }
+
+    public DataSourceSDKException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public DataSourceSDKException(Throwable cause) {
+        super(cause);
+    }
+
+    public DataSourceSDKException(String message, Object... args) {
+        super(String.format(message, args));
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java
new file mode 100644
index 00000000..8b09bc35
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/PageQueryRequest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.seatunnel.datasource.request;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+import java.util.Map;
+
+@Data
+@SuperBuilder
+public class PageQueryRequest {
+
+    private Integer pageNum;
+
+    private Integer pageSize;
+
+    private String orderBy;
+
+    private String order;
+
+    private String search;
+
+    Map<String, Object> fuzzySearchParamMap;
+
+    Map<String, Object> exactSearchParamMap;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java
new file mode 100644
index 00000000..3d7c4220
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableCreateRequest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.seatunnel.datasource.request;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class VirtualTableCreateRequest {
+
+    private Long id;
+
+    private Long dataSourceId;
+
+    private String description;
+
+    private String databaseName;
+
+    private String tableName;
+
+    private List<VirtualTableFieldRequest> fields;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java
new file mode 100644
index 00000000..f2acb83c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableFieldRequest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.seatunnel.datasource.request;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+@Builder
+public class VirtualTableFieldRequest {
+
+    private String type;
+
+    private String name;
+
+    private String comment;
+
+    private Boolean primaryKey;
+
+    private String defaultValue;
+
+    private Boolean nullable;
+
+    private Map<String, String> properties;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java
new file mode 100644
index 00000000..0939687c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/request/VirtualTableUpdateRequest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.seatunnel.datasource.request;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class VirtualTableUpdateRequest {
+
+    private String description;
+
+    private String databaseName;
+
+    private String tableName;
+
+    private List<VirtualTableFieldRequest> fields;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java
new file mode 100644
index 00000000..831a2a64
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/BaseResponse.java
@@ -0,0 +1,38 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+import java.util.Date;
+
+@Data
+@SuperBuilder
+public class BaseResponse {
+
+    /** create time */
+    private Date createTime;
+
+    /** update time */
+    private Date updateTime;
+
+    private String createUserId;
+
+    private String updateUserId;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java
new file mode 100644
index 00000000..f489ad45
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/DataSourceDetailResponse.java
@@ -0,0 +1,38 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+import java.util.Map;
+
+@Data
+@SuperBuilder
+public class DataSourceDetailResponse extends BaseResponse {
+
+    private Long id;
+
+    private String dataSourceName;
+
+    private String pluginName;
+
+    private String comment;
+
+    private Map<String, String> params;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java
new file mode 100644
index 00000000..24e801cc
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageDataResponse.java
@@ -0,0 +1,32 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class PageDataResponse<T> {
+    private Integer pageNum;
+    private Integer pageSize;
+    private Integer total;
+    private List<T> datas;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java
new file mode 100644
index 00000000..10bab20c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/PageResultResponse.java
@@ -0,0 +1,36 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class PageResultResponse<T> {
+
+    private Integer pageNum;
+
+    private Integer pageSize;
+
+    private Integer total;
+
+    private Integer pages;
+
+    private List<T> dataList;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java
new file mode 100644
index 00000000..63ab5d57
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableDetailResponse.java
@@ -0,0 +1,42 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+import java.util.List;
+
+@Data
+@SuperBuilder
+public class VirtualTableDetailResponse extends BaseResponse {
+
+    private Long id;
+
+    private Long dataSourceId;
+
+    private String description;
+
+    private String type;
+
+    private String databaseName;
+
+    private String tableName;
+
+    private List<VirtualTableFieldResponse> fields;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java
new file mode 100644
index 00000000..5e8583ef
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableFieldResponse.java
@@ -0,0 +1,42 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+import java.util.Map;
+
+@Data
+@SuperBuilder
+public class VirtualTableFieldResponse {
+
+    private String type;
+
+    private String name;
+
+    private String comment;
+
+    private Boolean primaryKey;
+
+    private String defaultValue;
+
+    private Boolean nullable;
+
+    private Map<String, String> properties;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java
new file mode 100644
index 00000000..8e061ad4
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/response/VirtualTableResponse.java
@@ -0,0 +1,36 @@
+/*
+ * 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.seatunnel.datasource.response;
+
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+
+@Data
+@SuperBuilder
+public class VirtualTableResponse extends BaseResponse {
+
+    private Long id;
+
+    private Long dataSourceId;
+
+    private String description;
+
+    private String databaseName;
+
+    private String tableName;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java
new file mode 100644
index 00000000..9ab18325
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/main/java/org/apache/seatunnel/datasource/service/DataSourceService.java
@@ -0,0 +1,116 @@
+/*
+ * 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.seatunnel.datasource.service;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DataSourceService {
+
+    /**
+     * get all data source plugins
+     *
+     * @return data source plugins info
+     */
+    List<DataSourcePluginInfo> listAllDataSources();
+
+    /**
+     * get data source plugin fields
+     *
+     * @param pluginName data source name
+     * @return data source plugin fields
+     */
+    OptionRule queryDataSourceFieldByName(String pluginName);
+
+    /**
+     * get data source metadata fields
+     *
+     * @param pluginName data source name
+     * @return data source metadata fields
+     */
+    OptionRule queryMetadataFieldByName(String pluginName);
+
+    /**
+     * check data source params is valid and connectable
+     *
+     * @param parameters data source params eg mysql plugin key: url // jdbc url key: username key:
+     *     password other key...
+     * @return true if valid, false if invalid
+     */
+    /**
+     * we can use this method to check data source connectivity
+     *
+     * @param dataSourceParams data source params
+     * @return check result
+     */
+    Boolean checkDataSourceConnectivity(String pluginName, Map<String, String> datasourceParams);
+
+    /**
+     * get data source table names by database name
+     *
+     * @param pluginName    plugin name
+     * @param databaseName  database name
+     * @param requestParams connection params
+     * @return table names
+     */
+    List<String> getTables(
+        String pluginName, String databaseName, Map<String, String> requestParams);
+
+    /**
+     * get data source database names
+     *
+     * @param pluginName    plugin name
+     * @param requestParams connection params
+     * @return database names
+     */
+    List<String> getDatabases(String pluginName, Map<String, String> requestParams);
+
+    /**
+     * get data source table fields
+     *
+     * @param pluginName    plugin name
+     * @param requestParams connection params
+     * @param databaseName  database name
+     * @param tableName     table name
+     * @return table fields
+     */
+    List<TableField> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String databaseName,
+        String tableName);
+
+    /**
+     * get data source table fields
+     *
+     * @param pluginName    plugin name
+     * @param requestParams connection params
+     * @param databaseName  database name
+     * @param tableNames    table names
+     * @return table fields
+     */
+    Map<String, List<TableField>> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String databaseName,
+        List<String> tableNames);
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java
new file mode 100644
index 00000000..ece18206
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/com/whaleops/datasource/s3/S3DatasourceChannelTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.whaleops.datasource.s3;
+
+import org.apache.seatunnel.datasource.plugin.s3.S3DatasourceChannel;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+@Disabled
+class S3DatasourceChannelTest {
+    private static S3DatasourceChannel S3_DATASOURCE_CHANNEL = new S3DatasourceChannel();
+
+    @Test
+    void checkDataSourceConnectivity() {
+        Assertions.assertDoesNotThrow(
+            () -> {
+                S3_DATASOURCE_CHANNEL.checkDataSourceConnectivity("S3", createRequestParams());
+            });
+    }
+
+    private Map<String, String> createRequestParams() {
+        Map<String, String> requestParams =
+            new ImmutableMap.Builder<String, String>()
+                .put("bucket", "s3a://poc-kuke")
+                .put("fs.s3a.endpoint", "s3.cn-north-1.amazonaws.com.cn")
+                .put(
+                    "fs.s3a.aws.credentials.provider",
+                    "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider")
+                .put("access_key", "AKIAYYUV5DMXADXRBGTA")
+                .put("secret_key", "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")
+                .put("hadoop_s3_properties", "")
+                .build();
+        return requestParams;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java
new file mode 100644
index 00000000..e2877e88
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-client/src/test/java/org/apache/seatunnel/datasource/DataSourceClientTest.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class DataSourceClientTest {
+    private static final DataSourceClient DATA_SOURCE_CLIENT = new DataSourceClient();
+
+    @Test
+    public void listAllDataSources() {
+        Assertions.assertTrue(
+            DATA_SOURCE_CLIENT.listAllDataSources().stream()
+                .anyMatch(
+                    dataSourcePluginInfo ->
+                        StringUtils.equalsAnyIgnoreCase(
+                            dataSourcePluginInfo.getName(), "jdbc-mysql")));
+        Assertions.assertTrue(
+            DATA_SOURCE_CLIENT.listAllDataSources().stream()
+                .anyMatch(
+                    dataSourcePluginInfo ->
+                        StringUtils.equalsAnyIgnoreCase(
+                            dataSourcePluginInfo.getName(), "kafka")));
+        Assertions.assertTrue(
+            DATA_SOURCE_CLIENT.listAllDataSources().stream()
+                .anyMatch(
+                    dataSourcePluginInfo ->
+                        StringUtils.equalsAnyIgnoreCase(
+                            dataSourcePluginInfo.getName(), "elasticsearch")));
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml
new file mode 100644
index 00000000..64d904df
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-all/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-all</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-clickhouse</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-hive</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-mysql</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-oracle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-postgresql</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-tidb</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-redshift</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-sqlserver</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-starrocks</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-kafka</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-elasticsearch</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-s3-redshift</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-starrocks</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-mysql-cdc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-s3</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-sqlserver-cdc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml
new file mode 100644
index 00000000..7d2272b7
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-elasticsearch</artifactId>
+
+    <properties>
+        <elasticsearch-rest-client.version>7.5.1</elasticsearch-rest-client.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-client</artifactId>
+            <version>${elasticsearch-rest-client.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>security</artifactId>
+            <version>206</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java
new file mode 100644
index 00000000..17495487
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannel.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.elasticsearch;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.elasticsearch.client.EsRestClient;
+
+import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ElasticSearchDataSourceChannel implements DataSourceChannel {
+
+    private static final String DATABASE = "default";
+
+    @Override
+    public boolean canAbleGetSchema() {
+        return true;
+    }
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return ElasticSearchOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return ElasticSearchOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        databaseCheck(database);
+        try (EsRestClient client =
+                 EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) {
+            return client.listIndex();
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        return DEFAULT_DATABASES;
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (EsRestClient client =
+                 EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) {
+            client.getClusterInfo();
+            return true;
+        } catch (Throwable e) {
+            throw new DataSourcePluginException(
+                "check ElasticSearch connectivity failed, " + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        databaseCheck(database);
+        try (EsRestClient client =
+                 EsRestClient.createInstance(ConfigFactory.parseMap(requestParams))) {
+            Map<String, String> fieldTypeMapping = client.getFieldTypeMapping(table);
+            List<TableField> fields = new ArrayList<>();
+            fieldTypeMapping.forEach(
+                (fieldName, fieldType) ->
+                    fields.add(convertToTableField(fieldName, fieldType)));
+            return fields;
+        } catch (Exception ex) {
+            throw new DataSourcePluginException("Get table fields failed", ex);
+        }
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        databaseCheck(database);
+        Map<String, List<TableField>> tableFields = new HashMap<>();
+        tables.forEach(
+            table ->
+                tableFields.put(
+                    table, getTableFields(pluginName, requestParams, database, table)));
+        return tableFields;
+    }
+
+    private static void databaseCheck(@NonNull String database) {
+        if (!StringUtils.equalsAnyIgnoreCase(database, DATABASE)) {
+            throw new IllegalArgumentException("database not found: " + database);
+        }
+    }
+
+    private TableField convertToTableField(String fieldName, String fieldType) {
+        TableField tableField = new TableField();
+        tableField.setName(fieldName);
+        tableField.setType(fieldType);
+        tableField.setComment(null);
+        tableField.setNullable(true);
+        tableField.setPrimaryKey(fieldName.equals("_id"));
+        tableField.setDefaultValue(null);
+        return tableField;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java
new file mode 100644
index 00000000..97eec189
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceConfig.java
@@ -0,0 +1,36 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+public class ElasticSearchDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "ElasticSearch";
+
+    public static final String PLUGIN_VERSION = "1.0.0";
+
+    public static final DataSourcePluginInfo ELASTICSEARCH_DATASOURCE_PLUGIN_INFO =
+            DataSourcePluginInfo.builder()
+                    .name(PLUGIN_NAME)
+                    .icon(PLUGIN_NAME)
+                    .version(PLUGIN_VERSION)
+                    .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode())
+                    .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java
new file mode 100644
index 00000000..f9d01cae
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.elasticsearch;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class ElasticSearchDataSourceFactory implements DataSourceFactory {
+
+    public static final String PLUGIN_NAME = "ElasticSearch";
+
+    @Override
+    public String factoryIdentifier() {
+        return PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(ElasticSearchDataSourceConfig.ELASTICSEARCH_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new ElasticSearchDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java
new file mode 100644
index 00000000..51097879
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchOptionRule.java
@@ -0,0 +1,109 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+import java.util.List;
+
+public class ElasticSearchOptionRule {
+
+    public static final Option<List<String>> HOSTS =
+            Options.key("hosts")
+                    .listType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "Elasticsearch cluster http address, the format is host:port, allowing multiple hosts to be specified. Such as [\"host1:9200\", \"host2:9200\"]");
+
+    public static final Option<String> INDEX =
+            Options.key("index")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Elasticsearch index name, support * fuzzy matching");
+
+    public static final Option<String> USERNAME =
+            Options.key("username")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("x-pack username");
+
+    public static final Option<String> PASSWORD =
+            Options.key("password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("x-pack password");
+
+    public static final Option<Boolean> TLS_VERIFY_CERTIFICATE =
+            Options.key("tls_verify_certificate")
+                    .booleanType()
+                    .defaultValue(true)
+                    .withDescription("Enable certificates validation for HTTPS endpoints");
+
+    public static final Option<Boolean> TLS_VERIFY_HOSTNAME =
+            Options.key("tls_verify_hostname")
+                    .booleanType()
+                    .defaultValue(true)
+                    .withDescription("Enable hostname validation for HTTPS endpoints");
+
+    public static final Option<String> TLS_KEY_STORE_PATH =
+            Options.key("tls_keystore_path")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "The path to the PEM or JKS key store. This file must be readable by the operating system user running SeaTunnel.");
+
+    public static final Option<String> TLS_KEY_STORE_PASSWORD =
+            Options.key("tls_keystore_password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("The key password for the key store specified");
+
+    public static final Option<String> TLS_TRUST_STORE_PATH =
+            Options.key("tls_truststore_path")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "The path to PEM or JKS trust store. This file must be readable by the operating system user running SeaTunnel.");
+
+    public static final Option<String> TLS_TRUST_STORE_PASSWORD =
+            Options.key("tls_truststore_password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("The key password for the trust store specified");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder()
+                .required(HOSTS)
+                .optional(
+                        USERNAME,
+                        PASSWORD,
+                        TLS_VERIFY_CERTIFICATE,
+                        TLS_VERIFY_HOSTNAME,
+                        TLS_KEY_STORE_PATH,
+                        TLS_KEY_STORE_PASSWORD,
+                        TLS_TRUST_STORE_PATH,
+                        TLS_TRUST_STORE_PASSWORD)
+                .build();
+    }
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder().required(INDEX).build();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java
new file mode 100644
index 00000000..f02516ce
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ElasticsearchClusterInfo.java
@@ -0,0 +1,30 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch.client;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+
+@Getter
+@Builder
+@ToString
+public class ElasticsearchClusterInfo {
+    private String distribution;
+    private String clusterVersion;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java
new file mode 100644
index 00000000..1ef2e705
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/EsRestClient.java
@@ -0,0 +1,371 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch.client;
+
+import org.apache.seatunnel.common.utils.JsonUtils;
+import org.apache.seatunnel.datasource.plugin.elasticsearch.ElasticSearchOptionRule;
+
+import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.JsonNode;
+import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.seatunnel.shade.com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.seatunnel.shade.com.typesafe.config.Config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.TrustAllStrategy;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+import org.elasticsearch.client.Request;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+
+import javax.net.ssl.SSLContext;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class EsRestClient implements AutoCloseable {
+
+    private static final int CONNECTION_REQUEST_TIMEOUT = 10 * 1000;
+
+    private static final int SOCKET_TIMEOUT = 5 * 60 * 1000;
+
+    private final RestClient restClient;
+
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    private EsRestClient(RestClient restClient) {
+        this.restClient = restClient;
+    }
+
+    public static EsRestClient createInstance(Config pluginConfig) {
+        try {
+            List<String> hosts =
+                OBJECT_MAPPER.readValue(
+                    pluginConfig.getString(ElasticSearchOptionRule.HOSTS.key()),
+                    List.class);
+            Optional<String> username = Optional.empty();
+            Optional<String> password = Optional.empty();
+            if (pluginConfig.hasPath(ElasticSearchOptionRule.USERNAME.key())) {
+                username =
+                    Optional.of(pluginConfig.getString(ElasticSearchOptionRule.USERNAME.key()));
+                if (pluginConfig.hasPath(ElasticSearchOptionRule.PASSWORD.key())) {
+                    password =
+                        Optional.of(
+                            pluginConfig.getString(ElasticSearchOptionRule.PASSWORD.key()));
+                }
+            }
+            Optional<String> keystorePath = Optional.empty();
+            Optional<String> keystorePassword = Optional.empty();
+            Optional<String> truststorePath = Optional.empty();
+            Optional<String> truststorePassword = Optional.empty();
+            boolean tlsVerifyCertificate =
+                ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.defaultValue();
+            if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key())) {
+                tlsVerifyCertificate =
+                    pluginConfig.getBoolean(
+                        ElasticSearchOptionRule.TLS_VERIFY_CERTIFICATE.key());
+            }
+            if (tlsVerifyCertificate) {
+                if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key())) {
+                    keystorePath =
+                        Optional.of(
+                            pluginConfig.getString(
+                                ElasticSearchOptionRule.TLS_KEY_STORE_PATH.key()));
+                }
+                if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key())) {
+                    keystorePassword =
+                        Optional.of(
+                            pluginConfig.getString(
+                                ElasticSearchOptionRule.TLS_KEY_STORE_PASSWORD.key()));
+                }
+                if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key())) {
+                    truststorePath =
+                        Optional.of(
+                            pluginConfig.getString(
+                                ElasticSearchOptionRule.TLS_TRUST_STORE_PATH.key()));
+                }
+                if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD.key())) {
+                    truststorePassword =
+                        Optional.of(
+                            pluginConfig.getString(
+                                ElasticSearchOptionRule.TLS_TRUST_STORE_PASSWORD
+                                    .key()));
+                }
+            }
+            boolean tlsVerifyHostnames = ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.defaultValue();
+            if (pluginConfig.hasPath(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key())) {
+                tlsVerifyHostnames =
+                    pluginConfig.getBoolean(ElasticSearchOptionRule.TLS_VERIFY_HOSTNAME.key());
+            }
+            return createInstance(
+                hosts,
+                username,
+                password,
+                tlsVerifyCertificate,
+                tlsVerifyHostnames,
+                keystorePath,
+                keystorePassword,
+                truststorePath,
+                truststorePassword);
+        } catch (Exception e) {
+            throw new RuntimeException("Create EsRestClient failed", e);
+        }
+    }
+
+    public static EsRestClient createInstance(
+        List<String> hosts,
+        Optional<String> username,
+        Optional<String> password,
+        boolean tlsVerifyCertificate,
+        boolean tlsVerifyHostnames,
+        Optional<String> keystorePath,
+        Optional<String> keystorePassword,
+        Optional<String> truststorePath,
+        Optional<String> truststorePassword) {
+        RestClientBuilder restClientBuilder =
+            getRestClientBuilder(
+                hosts,
+                username,
+                password,
+                tlsVerifyCertificate,
+                tlsVerifyHostnames,
+                keystorePath,
+                keystorePassword,
+                truststorePath,
+                truststorePassword);
+        return new EsRestClient(restClientBuilder.build());
+    }
+
+    private static RestClientBuilder getRestClientBuilder(
+        List<String> hosts,
+        Optional<String> username,
+        Optional<String> password,
+        boolean tlsVerifyCertificate,
+        boolean tlsVerifyHostnames,
+        Optional<String> keystorePath,
+        Optional<String> keystorePassword,
+        Optional<String> truststorePath,
+        Optional<String> truststorePassword) {
+        HttpHost[] httpHosts = new HttpHost[hosts.size()];
+        for (int i = 0; i < hosts.size(); i++) {
+            httpHosts[i] = HttpHost.create(hosts.get(i));
+        }
+
+        RestClientBuilder restClientBuilder =
+            RestClient.builder(httpHosts)
+                .setRequestConfigCallback(
+                    requestConfigBuilder ->
+                        requestConfigBuilder
+                            .setConnectionRequestTimeout(
+                                CONNECTION_REQUEST_TIMEOUT)
+                            .setSocketTimeout(SOCKET_TIMEOUT));
+
+        restClientBuilder.setHttpClientConfigCallback(
+            httpClientBuilder -> {
+                if (username.isPresent()) {
+                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+                    credentialsProvider.setCredentials(
+                        AuthScope.ANY,
+                        new UsernamePasswordCredentials(username.get(), password.get()));
+                    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
+                }
+
+                try {
+                    if (tlsVerifyCertificate) {
+                        Optional<SSLContext> sslContext =
+                            SSLUtils.buildSSLContext(
+                                keystorePath,
+                                keystorePassword,
+                                truststorePath,
+                                truststorePassword);
+                        sslContext.ifPresent(e -> httpClientBuilder.setSSLContext(e));
+                    } else {
+                        SSLContext sslContext =
+                            SSLContexts.custom()
+                                .loadTrustMaterial(new TrustAllStrategy())
+                                .build();
+                        httpClientBuilder.setSSLContext(sslContext);
+                    }
+                    if (!tlsVerifyHostnames) {
+                        httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
+                    }
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+                return httpClientBuilder;
+            });
+        return restClientBuilder;
+    }
+
+    public ElasticsearchClusterInfo getClusterInfo() {
+        Request request = new Request("GET", "/");
+        try {
+            Response response = restClient.performRequest(request);
+            String result = EntityUtils.toString(response.getEntity());
+            ObjectMapper objectMapper = new ObjectMapper();
+            JsonNode jsonNode = objectMapper.readTree(result);
+            JsonNode versionNode = jsonNode.get("version");
+            return ElasticsearchClusterInfo.builder()
+                .clusterVersion(versionNode.get("number").asText())
+                .distribution(
+                    Optional.ofNullable(versionNode.get("distribution"))
+                        .map(JsonNode::asText)
+                        .orElse(null))
+                .build();
+        } catch (IOException e) {
+            throw new ResponseException("fail to get elasticsearch version.", e);
+        }
+    }
+
+    public void close() {
+        try {
+            restClient.close();
+        } catch (IOException e) {
+            log.warn("close elasticsearch connection error", e);
+        }
+    }
+
+    public List<String> listIndex() {
+        String endpoint = "/_cat/indices?format=json";
+        Request request = new Request("GET", endpoint);
+        try {
+            Response response = restClient.performRequest(request);
+            if (response == null) {
+                throw new ResponseException("GET " + endpoint + " response null");
+            }
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String entity = EntityUtils.toString(response.getEntity());
+                return JsonUtils.toList(entity, Map.class).stream()
+                    .map(map -> map.get("index").toString())
+                    .collect(Collectors.toList());
+            } else {
+                throw new ResponseException(
+                    String.format(
+                        "GET %s response status code=%d",
+                        endpoint, response.getStatusLine().getStatusCode()));
+            }
+        } catch (IOException ex) {
+            throw new ResponseException(ex);
+        }
+    }
+
+    public void dropIndex(String tableName) {
+        String endpoint = String.format("/%s", tableName);
+        Request request = new Request("DELETE", endpoint);
+        try {
+            Response response = restClient.performRequest(request);
+            if (response == null) {
+                throw new ResponseException("DELETE " + endpoint + " response null");
+            }
+            // todo: if the index doesn't exist, the response status code is 200?
+            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+                throw new ResponseException(
+                    String.format(
+                        "DELETE %s response status code=%d",
+                        endpoint, response.getStatusLine().getStatusCode()));
+            }
+        } catch (IOException ex) {
+            throw new ResponseException(ex);
+        }
+    }
+
+    /**
+     * get es field name and type mapping relation
+     *
+     * @param index index name
+     * @return {key-> field name,value->es type}
+     */
+    public Map<String, String> getFieldTypeMapping(String index) {
+        String endpoint = String.format("/%s/_mappings", index);
+        Request request = new Request("GET", endpoint);
+        Map<String, String> mapping = new HashMap<>();
+        try {
+            Response response = restClient.performRequest(request);
+            if (response == null) {
+                throw new ResponseException("GET " + endpoint + " response null");
+            }
+            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+                throw new ResponseException(
+                    String.format(
+                        "GET %s response status code=%d",
+                        endpoint, response.getStatusLine().getStatusCode()));
+            }
+            String entity = EntityUtils.toString(response.getEntity());
+            log.info(String.format("GET %s response=%s", endpoint, entity));
+            ObjectNode responseJson = JsonUtils.parseObject(entity);
+            for (Iterator<JsonNode> it = responseJson.elements(); it.hasNext(); ) {
+                JsonNode indexProperty = it.next();
+                JsonNode mappingsProperty = indexProperty.get("mappings");
+                if (mappingsProperty.has("mappingsProperty")) {
+                    JsonNode properties = mappingsProperty.get("properties");
+                    mapping = getFieldTypeMappingFromProperties(properties);
+                } else {
+                    for (JsonNode typeNode : mappingsProperty) {
+                        JsonNode properties;
+                        if (typeNode.has("properties")) {
+                            properties = typeNode.get("properties");
+                        } else {
+                            properties = typeNode;
+                        }
+                        mapping.putAll(getFieldTypeMappingFromProperties(properties));
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            throw new ResponseException(ex);
+        }
+        return mapping;
+    }
+
+    private static Map<String, String> getFieldTypeMappingFromProperties(JsonNode properties) {
+        Map<String, String> mapping = new HashMap<>();
+        for (Iterator<String> it = properties.fieldNames(); it.hasNext(); ) {
+            String field = it.next();
+            JsonNode fieldProperty = properties.get(field);
+            if (fieldProperty == null) {
+                mapping.put(field, "text");
+            } else {
+                if (fieldProperty.has("type")) {
+                    String type = fieldProperty.get("type").asText();
+                    mapping.put(field, type);
+                } else {
+                    log.warn(
+                        String.format(
+                            "fail to get elasticsearch field %s mapping type,so give a default type text",
+                            field));
+                    mapping.put(field, "text");
+                }
+            }
+        }
+        return mapping;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java
new file mode 100644
index 00000000..59417cb7
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/ResponseException.java
@@ -0,0 +1,45 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch.client;
+
+public class ResponseException extends RuntimeException {
+
+    public ResponseException() {
+        super();
+    }
+
+    public ResponseException(String message) {
+        super(message);
+    }
+
+    public ResponseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ResponseException(Throwable cause) {
+        super(cause);
+    }
+
+    protected ResponseException(
+            String message,
+            Throwable cause,
+            boolean enableSuppression,
+            boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java
new file mode 100644
index 00000000..9c10cd3b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/main/java/org/apache/seatunnel/datasource/plugin/elasticsearch/client/SSLUtils.java
@@ -0,0 +1,165 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch.client;
+
+import static java.util.Collections.list;
+
+import io.airlift.security.pem.PemReader;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+@SuppressWarnings("MagicNumber")
+public final class SSLUtils {
+
+    public static Optional<SSLContext> buildSSLContext(
+        Optional<String> keyStorePath,
+        Optional<String> keyStorePassword,
+        Optional<String> trustStorePath,
+        Optional<String> trustStorePassword)
+        throws GeneralSecurityException, IOException {
+        if (!keyStorePath.isPresent() && !trustStorePath.isPresent()) {
+            return Optional.empty();
+        }
+        return Optional.of(
+            createSSLContext(
+                keyStorePath, keyStorePassword, trustStorePath, trustStorePassword));
+    }
+
+    private static SSLContext createSSLContext(
+        Optional<String> keyStorePath,
+        Optional<String> keyStorePassword,
+        Optional<String> trustStorePath,
+        Optional<String> trustStorePassword)
+        throws GeneralSecurityException, IOException {
+        // load KeyStore if configured and get KeyManagers
+        KeyStore keyStore = null;
+        KeyManager[] keyManagers = null;
+        if (keyStorePath.isPresent()) {
+            File keyStoreFile = new File(keyStorePath.get());
+            char[] keyManagerPassword;
+            try {
+                // attempt to read the key store as a PEM file
+                keyStore = PemReader.loadKeyStore(keyStoreFile, keyStoreFile, keyStorePassword);
+                // for PEM encoded keys, the password is used to decrypt the specific key (and does
+                // not protect the keystore itself)
+                keyManagerPassword = new char[0];
+            } catch (IOException | GeneralSecurityException ignored) {
+                keyManagerPassword = keyStorePassword.map(String::toCharArray).orElse(null);
+
+                keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+                try (InputStream in = new FileInputStream(keyStoreFile)) {
+                    keyStore.load(in, keyManagerPassword);
+                }
+            }
+            validateCertificates(keyStore);
+            KeyManagerFactory keyManagerFactory =
+                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            keyManagerFactory.init(keyStore, keyManagerPassword);
+            keyManagers = keyManagerFactory.getKeyManagers();
+        }
+
+        // load TrustStore if configured, otherwise use KeyStore
+        KeyStore trustStore = keyStore;
+        if (trustStorePath.isPresent()) {
+            File trustStoreFile = new File(trustStorePath.get());
+            trustStore = loadTrustStore(trustStoreFile, trustStorePassword);
+        }
+
+        // create TrustManagerFactory
+        TrustManagerFactory trustManagerFactory =
+            TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        trustManagerFactory.init(trustStore);
+
+        // get X509TrustManager
+        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+            throw new RuntimeException(
+                "Unexpected default trust managers:" + Arrays.toString(trustManagers));
+        }
+        // create SSLContext
+        SSLContext result = SSLContext.getInstance("SSL");
+        result.init(keyManagers, trustManagers, null);
+        return result;
+    }
+
+    private static KeyStore loadTrustStore(File trustStorePath, Optional<String> trustStorePassword)
+        throws IOException, GeneralSecurityException {
+        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        try {
+            // attempt to read the trust store as a PEM file
+            List<X509Certificate> certificateChain = PemReader.readCertificateChain(trustStorePath);
+            if (!certificateChain.isEmpty()) {
+                trustStore.load(null, null);
+                for (X509Certificate certificate : certificateChain) {
+                    X500Principal principal = certificate.getSubjectX500Principal();
+                    trustStore.setCertificateEntry(principal.getName(), certificate);
+                }
+                return trustStore;
+            }
+        } catch (IOException | GeneralSecurityException ignored) {
+            // ignored
+        }
+
+        try (InputStream in = new FileInputStream(trustStorePath)) {
+            trustStore.load(in, trustStorePassword.map(String::toCharArray).orElse(null));
+        }
+        return trustStore;
+    }
+
+    private static void validateCertificates(KeyStore keyStore) throws GeneralSecurityException {
+        for (String alias : list(keyStore.aliases())) {
+            if (!keyStore.isKeyEntry(alias)) {
+                continue;
+            }
+            Certificate certificate = keyStore.getCertificate(alias);
+            if (!(certificate instanceof X509Certificate)) {
+                continue;
+            }
+
+            try {
+                ((X509Certificate) certificate).checkValidity();
+            } catch (CertificateExpiredException e) {
+                throw new CertificateExpiredException(
+                    "KeyStore certificate is expired: " + e.getMessage());
+            } catch (CertificateNotYetValidException e) {
+                throw new CertificateNotYetValidException(
+                    "KeyStore certificate is not yet valid: " + e.getMessage());
+            }
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java
new file mode 100644
index 00000000..b8b25d84
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceChannelTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch;
+
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+// todo: use testcontainer to create container
+@Disabled
+class ElasticSearchDataSourceChannelTest {
+    private static final Logger LOGGER =
+        LoggerFactory.getLogger(ElasticSearchDataSourceChannelTest.class);
+
+    private static final ElasticSearchDataSourceChannel ELASTIC_SEARCH_DATA_SOURCE_CHANNEL =
+        new ElasticSearchDataSourceChannel();
+
+    private static final String PLUGIN_NAME = "ElasticSearch";
+
+    private static final String DATABASE = "Default";
+
+    private static final Map<String, String> REQUEST_MAP =
+        new ImmutableMap.Builder<String, String>()
+            .put(ElasticSearchOptionRule.HOSTS.key(), "[\"http://localhost:9200\"]")
+            .build();
+
+    @Test
+    void canAbleGetSchema() {
+        Assertions.assertTrue(ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.canAbleGetSchema());
+    }
+
+    @Test
+    void getDataSourceOptions() {
+        Assertions.assertNotNull(ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDataSourceOptions(PLUGIN_NAME));
+    }
+
+    @Test
+    void getDatasourceMetadataFieldsByDataSourceName() {
+        Assertions.assertNotNull(
+            ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName(
+                PLUGIN_NAME));
+    }
+
+    @Test
+    void getTables() {
+        Assertions.assertDoesNotThrow(
+            () -> {
+                List<String> tables =
+                    ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTables(
+                        PLUGIN_NAME, REQUEST_MAP, DATABASE);
+                LOGGER.info("{}", tables);
+            });
+    }
+
+    @Test
+    void getDatabases() {
+        Assertions.assertLinesMatch(
+            Lists.newArrayList("default"),
+            ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getDatabases(PLUGIN_NAME, REQUEST_MAP));
+    }
+
+    @Test
+    void checkDataSourceConnectivity() {
+        Assertions.assertTrue(
+            ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity(
+                PLUGIN_NAME, REQUEST_MAP));
+    }
+
+    @Test
+    void getTableFields() {
+        Assertions.assertDoesNotThrow(
+            () -> {
+                List<TableField> tableFields =
+                    ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields(
+                        PLUGIN_NAME, REQUEST_MAP, DATABASE, "");
+                LOGGER.info("{}", tableFields);
+            });
+    }
+
+    @Test
+    void testGetTableFields() {
+        Assertions.assertDoesNotThrow(
+            () -> {
+                Map<String, List<TableField>> tableFields =
+                    ELASTIC_SEARCH_DATA_SOURCE_CHANNEL.getTableFields(
+                        PLUGIN_NAME, REQUEST_MAP, DATABASE, Lists.newArrayList(""));
+                LOGGER.info("{}", tableFields);
+            });
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java
new file mode 100644
index 00000000..85a49e77
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-elasticsearch/src/test/java/org/apache/seatunnel/datasource/plugin/elasticsearch/ElasticSearchDataSourceFactoryTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.seatunnel.datasource.plugin.elasticsearch;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class ElasticSearchDataSourceFactoryTest {
+
+    private static final ElasticSearchDataSourceFactory ELASTIC_SEARCH_DATA_SOURCE_FACTORY =
+        new ElasticSearchDataSourceFactory();
+
+    @Test
+    void factoryIdentifier() {
+        Assertions.assertEquals(
+            "ElasticSearch", ELASTIC_SEARCH_DATA_SOURCE_FACTORY.factoryIdentifier());
+    }
+
+    @Test
+    void supportedDataSources() {
+        Assertions.assertFalse(ELASTIC_SEARCH_DATA_SOURCE_FACTORY.supportedDataSources().isEmpty());
+    }
+
+    @Test
+    void createChannel() {
+        Assertions.assertNotNull(ELASTIC_SEARCH_DATA_SOURCE_FACTORY.createChannel());
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml
new file mode 100644
index 00000000..1ca92dfb
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-clickhouse</artifactId>
+
+    <properties>
+        <clickhouse.version>0.3.2-patch11</clickhouse.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>com.clickhouse</groupId>
+            <artifactId>clickhouse-jdbc</artifactId>
+            <version>${clickhouse.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java
new file mode 100644
index 00000000..4862d41d
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseDataSourceConfig.java
@@ -0,0 +1,59 @@
+/*
+ * 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.seatunnel.datasource.plugin.clickhouse.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class ClickhouseDataSourceConfig {
+    public static final String PLUGIN_NAME = "JDBC-ClickHouse";
+
+    public static final DataSourcePluginInfo CLICKHOUSE_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .supportVirtualTables(false)
+            .build();
+
+    public static final Set<String> CLICKHOUSE_SYSTEM_DATABASES =
+        Sets.newHashSet(
+            "system",
+            "default",
+            "information_schema",
+            "mysql",
+            "performance_schema",
+            "sys");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(ClickhouseOptionRule.URL, ClickhouseOptionRule.DRIVER)
+            .optional(ClickhouseOptionRule.USER, ClickhouseOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(ClickhouseOptionRule.DATABASE, ClickhouseOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java
new file mode 100644
index 00000000..37141174
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceChannel.java
@@ -0,0 +1,177 @@
+/*
+ * 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.seatunnel.datasource.plugin.clickhouse.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class ClickhouseJdbcDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return ClickhouseDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return ClickhouseDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);) {
+            ResultSet resultSet =
+                connection
+                    .getMetaData()
+                    .getTables(database, null, null, new String[]{"TABLE"});
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             Statement statement = connection.createStatement();
+             ResultSet re = statement.executeQuery("SHOW DATABASES;")) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("name");
+                if (StringUtils.isNotBlank(dbName)
+                    && !ClickhouseDataSourceConfig.CLICKHOUSE_SYSTEM_DATABASES.contains(
+                    dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (Exception ex) {
+            throw new RuntimeException("get databases failed", ex);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("Check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("Get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(ClickhouseOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(ClickhouseOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(ClickhouseOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(ClickhouseOptionRule.USER.key())) {
+            String username = requestParams.get(ClickhouseOptionRule.USER.key());
+            String password = requestParams.get(ClickhouseOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java
new file mode 100644
index 00000000..f8b97986
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseJdbcDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.clickhouse.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class ClickhouseJdbcDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return ClickhouseDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(ClickhouseDataSourceConfig.CLICKHOUSE_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new ClickhouseJdbcDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java
new file mode 100644
index 00000000..3beff605
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-clickhouse/src/main/java/org/apache/seatunnel/datasource/plugin/clickhouse/jdbc/ClickhouseOptionRule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.seatunnel.datasource.plugin.clickhouse.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class ClickhouseOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + "jdbc:clickhouse://localhost:8123/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .noDefaultValue()
+            .withDescription("driver");
+
+    public enum DriverType {
+        ClickHouse("ru.yandex.clickhouse.ClickHouseDriver");
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml
new file mode 100644
index 00000000..c73b4502
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-hive</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java
new file mode 100644
index 00000000..8b133ac7
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcConstants.java
@@ -0,0 +1,29 @@
+/*
+ * 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.seatunnel.datasource.plugin.hive.jdbc;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class HiveJdbcConstants {
+
+    public static final Set<String> HIVE_SYSTEM_DATABASES =
+            Sets.newHashSet(
+                    "information_schema", "mysql", "performance_schema", "sys", "test", "hivedb");
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java
new file mode 100644
index 00000000..32543c42
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceChannel.java
@@ -0,0 +1,215 @@
+/*
+ * 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.seatunnel.datasource.plugin.hive.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class HiveJdbcDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return HiveJdbcOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return HiveJdbcOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        return getTables(pluginName, requestParams, database);
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try {
+            return getDataBaseNames(pluginName, requestParams);
+        } catch (SQLException e) {
+            log.error("Query Hive databases error, request params is {}", requestParams, e);
+            throw new DataSourcePluginException("Query Hive databases error,", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        return checkJdbcConnectivity(requestParams);
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        return getTableFields(requestParams, database, table);
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        Map<String, List<TableField>> tableFields = new HashMap<>(tables.size());
+        for (String table : tables) {
+            tableFields.put(table, getTableFields(requestParams, database, table));
+        }
+        return tableFields;
+    }
+
+    protected boolean checkJdbcConnectivity(Map<String, String> requestParams) {
+        try (Connection ignored = init(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException(
+                "check jdbc connectivity failed, " + e.getMessage(), e);
+        }
+    }
+
+    protected Connection init(Map<String, String> requestParams) throws SQLException {
+        if (MapUtils.isEmpty(requestParams)) {
+            throw new DataSourcePluginException(
+                "Hive jdbc request params is null, please check your config");
+        }
+        String url = requestParams.get(HiveJdbcOptionRule.URL.key());
+        return DriverManager.getConnection(url);
+    }
+
+    protected List<String> getDataBaseNames(String pluginName, Map<String, String> requestParams)
+        throws SQLException {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);
+             Statement statement = connection.createStatement();) {
+            ResultSet re = statement.executeQuery("SHOW DATABASES;");
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(pluginName, dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        }
+    }
+
+    protected List<String> getTableNames(Map<String, String> requestParams, String dbName) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);) {
+            ResultSet resultSet =
+                connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"});
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    protected List<TableField> getTableFields(
+        Map<String, String> requestParams, String dbName, String tableName) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = init(requestParams);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, dbName, tableName);
+            ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null);
+            while (resultSet.next()) {
+                TableField tableField = new TableField();
+                String columnName = resultSet.getString("COLUMN_NAME");
+                tableField.setPrimaryKey(false);
+                if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                    tableField.setPrimaryKey(true);
+                }
+                tableField.setName(columnName);
+                tableField.setType(resultSet.getString("TYPE_NAME"));
+                tableField.setComment(resultSet.getString("REMARKS"));
+                Object nullable = resultSet.getObject("IS_NULLABLE");
+                boolean isNullable = convertToBoolean(nullable);
+                tableField.setNullable(isNullable);
+                tableFields.add(tableField);
+            }
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    @SuppressWarnings("checkstyle:MagicNumber")
+    private static boolean checkHostConnectable(String host, int port) {
+        try (Socket socket = new Socket()) {
+            socket.connect(new InetSocketAddress(host, port), 1000);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    private boolean isNotSystemDatabase(String pluginName, String dbName) {
+        // FIXME,filters system databases
+        return true;
+    }
+
+    private boolean convertToBoolean(Object value) {
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        if (value instanceof String) {
+            return value.equals("TRUE");
+        }
+        return false;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java
new file mode 100644
index 00000000..b149cc3b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcDataSourceFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seatunnel.datasource.plugin.hive.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class HiveJdbcDataSourceFactory implements DataSourceFactory {
+    @Override
+    public String factoryIdentifier() {
+        return "Hive-JDBC";
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        DataSourcePluginInfo dataSourcePluginInfo =
+                DataSourcePluginInfo.builder()
+                        .name("Hive-JDBC")
+                        .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+                        .version("1.0.0")
+                        .icon("Hive-JDBC")
+                        .supportVirtualTables(false)
+                        .build();
+        Set<DataSourcePluginInfo> dataSourceInfos = new HashSet<>();
+        dataSourceInfos.add(dataSourcePluginInfo);
+        return dataSourceInfos;
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new HiveJdbcDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java
new file mode 100644
index 00000000..fb004b69
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-hive/src/main/java/org/apache/seatunnel/datasource/plugin/hive/jdbc/HiveJdbcOptionRule.java
@@ -0,0 +1,42 @@
+/*
+ * 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.seatunnel.datasource.plugin.hive.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+public class HiveJdbcOptionRule {
+
+    public static final Option<String> URL =
+            Options.key("url")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "jdbc url, eg:"
+                                    + "jdbc:hive2://localhost:10000/default?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder().required(URL).build();
+    }
+
+    public static OptionRule metadataRule() {
+        // todo
+        return OptionRule.builder().build();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml
new file mode 100644
index 00000000..11e0ea6b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-mysql</artifactId>
+
+    <properties>
+        <mysql-connector.version>8.0.28</mysql-connector.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql-connector.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java
new file mode 100644
index 00000000..2e2de3e4
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlDataSourceConfig.java
@@ -0,0 +1,51 @@
+/*
+ * 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.seatunnel.datasource.plugin.mysql.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class MysqlDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-Mysql";
+
+    public static final DataSourcePluginInfo MYSQL_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> MYSQL_SYSTEM_DATABASES =
+        Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(MysqlOptionRule.URL, MysqlOptionRule.DRIVER)
+            .optional(MysqlOptionRule.USER, MysqlOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder().required(MysqlOptionRule.DATABASE, MysqlOptionRule.TABLE).build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java
new file mode 100644
index 00000000..1fe420d5
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceChannel.java
@@ -0,0 +1,182 @@
+/*
+ * 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.seatunnel.datasource.plugin.mysql.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class MysqlJdbcDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return MysqlDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return MysqlDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(database, null, null, new String[]{"TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName)
+                    && !MysqlDataSourceConfig.MYSQL_SYSTEM_DATABASES.contains(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("Get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return tables.parallelStream()
+            .collect(
+                Collectors.toMap(
+                    Function.identity(),
+                    table ->
+                        getTableFields(
+                            pluginName, requestParams, database, table)));
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(MysqlOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(MysqlOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(MysqlOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(MysqlOptionRule.USER.key())) {
+            String username = requestParams.get(MysqlOptionRule.USER.key());
+            String password = requestParams.get(MysqlOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java
new file mode 100644
index 00000000..31f6439b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlJdbcDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.mysql.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class MysqlJdbcDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return MysqlDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(MysqlDataSourceConfig.MYSQL_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new MysqlJdbcDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java
new file mode 100644
index 00000000..f666251f
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-mysql/src/main/java/org/apache/seatunnel/datasource/plugin/mysql/jdbc/MysqlOptionRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seatunnel.datasource.plugin.mysql.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class MysqlOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.MYSQL)
+            .withDescription("driver");
+
+    public enum DriverType {
+        MYSQL("com.mysql.cj.jdbc.Driver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml
new file mode 100644
index 00000000..1e48c9b8
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-oracle</artifactId>
+
+    <properties>
+        <oracle-jdbc.version>21.5.0.0</oracle-jdbc.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>com.oracle.database.jdbc</groupId>
+            <artifactId>ojdbc8</artifactId>
+            <version>${oracle-jdbc.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java
new file mode 100644
index 00000000..91ccc661
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceChannel.java
@@ -0,0 +1,174 @@
+/*
+ * 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.seatunnel.datasource.plugin.oracle.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class OracleDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return OracleDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return OracleDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(database, null, null, new String[]{"TABLE"});) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName)
+                    && !OracleDataSourceConfig.ORACLE_SYSTEM_DATABASES.contains(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (Exception ex) {
+            throw new RuntimeException("get databases failed", ex);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(OracleOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(OracleOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(OracleOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(OracleOptionRule.USER.key())) {
+            String username = requestParams.get(OracleOptionRule.USER.key());
+            String password = requestParams.get(OracleOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java
new file mode 100644
index 00000000..96f09fbe
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleDataSourceConfig.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seatunnel.datasource.plugin.oracle.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class OracleDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-Oracle";
+
+    public static final DataSourcePluginInfo ORACLE_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> ORACLE_SYSTEM_DATABASES =
+        Sets.newHashSet("SYS", "SYSTEM", "SYSDBA", "SYSOPER", "HR", "SCOTT");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(OracleOptionRule.URL, OracleOptionRule.DRIVER)
+            .optional(OracleOptionRule.USER, OracleOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(OracleOptionRule.DATABASE, OracleOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java
new file mode 100644
index 00000000..b6a0aec9
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleJdbcDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.oracle.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class OracleJdbcDataSourceFactory implements DataSourceFactory {
+    @Override
+    public String factoryIdentifier() {
+        return OracleDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(OracleDataSourceConfig.ORACLE_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new OracleDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java
new file mode 100644
index 00000000..517a8c32
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-oracle/src/main/java/org/apache/seatunnel/datasource/plugin/oracle/jdbc/OracleOptionRule.java
@@ -0,0 +1,67 @@
+/*
+ * 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.seatunnel.datasource.plugin.oracle.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class OracleOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription("jdbc url, eg:" + "jdbc:oracle:thin:@localhost:1521:XE");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.ORACLE)
+            .withDescription("driver");
+
+    public enum DriverType {
+        ORACLE("oracle.jdbc.driver.OracleDriver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml
new file mode 100644
index 00000000..cffbf251
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-postgresql</artifactId>
+
+    <properties>
+        <postgresql.version>42.4.3</postgresql.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>${postgresql.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java
new file mode 100644
index 00000000..b331d61d
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceChannel.java
@@ -0,0 +1,184 @@
+/*
+ * 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.seatunnel.datasource.plugin.postgresql.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class PostgresqlDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return PostgresqlDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return PostgresqlDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        String query = "SELECT table_schema, table_name FROM information_schema.tables";
+        try (Connection connection = getConnection(requestParams, database)) {
+            try (Statement statement = connection.createStatement();
+                 ResultSet resultSet = statement.executeQuery(query)) {
+                while (resultSet.next()) {
+                    String schemaName = resultSet.getString("table_schema");
+                    String tableName = resultSet.getString("table_name");
+                    if (StringUtils.isNotBlank(schemaName)
+                        && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains(
+                        schemaName)) {
+                        tableNames.add(schemaName + "." + tableName);
+                    }
+                }
+            }
+            return tableNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement =
+                 connection.prepareStatement("select datname from pg_database;");
+             ResultSet re = statement.executeQuery()) {
+            while (re.next()) {
+                String dbName = re.getString("datname");
+                if (StringUtils.isNotBlank(dbName)
+                    && !PostgresqlDataSourceConfig.POSTGRESQL_SYSTEM_DATABASES.contains(
+                    dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            String[] split = table.split("\\.");
+            if (split.length != 2) {
+                throw new DataSourcePluginException(
+                    "Postgresql tableName should composed by schemaName.tableName");
+            }
+            try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(PostgresqlOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(PostgresqlOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(PostgresqlOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(PostgresqlOptionRule.USER.key())) {
+            String username = requestParams.get(PostgresqlOptionRule.USER.key());
+            String password = requestParams.get(PostgresqlOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java
new file mode 100644
index 00000000..2aa0538b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceConfig.java
@@ -0,0 +1,62 @@
+/*
+ * 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.seatunnel.datasource.plugin.postgresql.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class PostgresqlDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-Postgres";
+
+    public static final DataSourcePluginInfo POSTGRESQL_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> POSTGRESQL_SYSTEM_DATABASES =
+        Sets.newHashSet(
+            "information_schema",
+            "pg_catalog",
+            "root",
+            "pg_toast",
+            "pg_temp_1",
+            "pg_toast_temp_1",
+            "postgres",
+            "template0",
+            "template1");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(PostgresqlOptionRule.URL, PostgresqlOptionRule.DRIVER)
+            .optional(PostgresqlOptionRule.USER, PostgresqlOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(PostgresqlOptionRule.DATABASE, PostgresqlOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java
new file mode 100644
index 00000000..13dd7847
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlDataSourceFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.seatunnel.datasource.plugin.postgresql.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class PostgresqlDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return PostgresqlDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(PostgresqlDataSourceConfig.POSTGRESQL_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new PostgresqlDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java
new file mode 100644
index 00000000..16e8b85b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-postgresql/src/main/java/org/apache/seatunnel/datasource/plugin/postgresql/jdbc/PostgresqlOptionRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seatunnel.datasource.plugin.postgresql.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class PostgresqlOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + "jdbc:postgresql://localhost:5432//test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.POSTGRESQL)
+            .withDescription("driver");
+
+    public enum DriverType {
+        POSTGRESQL("org.postgresql.Driver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml
new file mode 100644
index 00000000..fc369127
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-redshift</artifactId>
+
+    <properties>
+        <redshift.version>2.1.0.10</redshift.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>com.amazon.redshift</groupId>
+            <artifactId>redshift-jdbc42</artifactId>
+            <version>${redshift.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java
new file mode 100644
index 00000000..32f77e98
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceChannel.java
@@ -0,0 +1,180 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class RedshiftDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return RedshiftDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return RedshiftDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database);
+             ResultSet resultSet =
+                 connection.getMetaData().getTables(database, null, null, null);) {
+            while (resultSet.next()) {
+                String schemaName = resultSet.getString("TABLE_SCHEM");
+                String tableName = resultSet.getString("TABLE_NAME");
+                // todo: use isNotSystemSchemaName
+                if (StringUtils.isNotBlank(schemaName)
+                    && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(schemaName)) {
+                    tableNames.add(schemaName + "." + tableName);
+                }
+            }
+            return tableNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement =
+                 connection.prepareStatement("select datname from pg_database;");
+             ResultSet re = statement.executeQuery()) {
+            while (re.next()) {
+                String dbName = re.getString("datname");
+                if (StringUtils.isNotBlank(dbName)
+                    && !RedshiftDataSourceConfig.REDSHIFT_SYSTEM_TABLES.contains(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            String[] split = table.split("\\.");
+            if (split.length != 2) {
+                throw new DataSourcePluginException(
+                    "Postgresql tableName should composed by schemaName.tableName");
+            }
+            try (ResultSet resultSet = metaData.getColumns(database, split[0], split[1], null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(RedshiftOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(RedshiftOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(RedshiftOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(RedshiftOptionRule.USER.key())) {
+            String username = requestParams.get(RedshiftOptionRule.USER.key());
+            String password = requestParams.get(RedshiftOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java
new file mode 100644
index 00000000..450c8d95
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceConfig.java
@@ -0,0 +1,62 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class RedshiftDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-Redshift";
+
+    public static final DataSourcePluginInfo REDSHIFT_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon("redshift")
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> REDSHIFT_SYSTEM_TABLES =
+        Sets.newHashSet(
+            "information_schema",
+            "pg_catalog",
+            "root",
+            "pg_toast",
+            "pg_temp_1",
+            "pg_toast_temp_1",
+            "postgres",
+            "template0",
+            "template1");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(RedshiftOptionRule.URL, RedshiftOptionRule.DRIVER)
+            .optional(RedshiftOptionRule.USER, RedshiftOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(RedshiftOptionRule.DATABASE, RedshiftOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java
new file mode 100644
index 00000000..bf33a24c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.redshift.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class RedshiftDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return RedshiftDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(RedshiftDataSourceConfig.REDSHIFT_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new RedshiftDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java
new file mode 100644
index 00000000..ba4c11c0
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/jdbc/RedshiftOptionRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class RedshiftOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + "jdbc:redshift://server.redshift.amazonaws.com:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.JDBC42_REDSHIFT)
+            .withDescription("driver");
+
+    public enum DriverType {
+        JDBC42_REDSHIFT("com.amazon.redshift.jdbc42.Driver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml
new file mode 100644
index 00000000..5452a7e8
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-sqlserver</artifactId>
+
+    <properties>
+        <sqlserver.version>9.2.1.jre8</sqlserver.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <version>${sqlserver.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java
new file mode 100644
index 00000000..bde7550c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceChannel.java
@@ -0,0 +1,175 @@
+/*
+ * 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.seatunnel.datasource.plugin.sqlserver.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class SqlServerDataSourceChannel implements DataSourceChannel {
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return SqlServerDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return SqlServerDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(database, null, null, new String[]{"TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName)
+                    && !SqlServerDataSourceConfig.SQLSERVER_SYSTEM_DATABASES.contains(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (Exception ex) {
+            throw new RuntimeException("get databases failed", ex);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(SqlServerOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(SqlServerOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(SqlServerOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(SqlServerOptionRule.USER.key())) {
+            String username = requestParams.get(SqlServerOptionRule.USER.key());
+            String password = requestParams.get(SqlServerOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java
new file mode 100644
index 00000000..3bfc056c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceConfig.java
@@ -0,0 +1,60 @@
+/*
+ * 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.seatunnel.datasource.plugin.sqlserver.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class SqlServerDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-SQLServer";
+
+    public static final DataSourcePluginInfo SQLSERVER_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> SQLSERVER_SYSTEM_DATABASES =
+        Sets.newHashSet(
+            "master",
+            "tempdb",
+            "model",
+            "msdb",
+            "ReportServer",
+            "ReportServerTempDB",
+            "SSISDB");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(SqlServerOptionRule.URL, SqlServerOptionRule.DRIVER)
+            .optional(SqlServerOptionRule.USER, SqlServerOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(SqlServerOptionRule.DATABASE, SqlServerOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java
new file mode 100644
index 00000000..b9a21ce4
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.sqlserver.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class SqlServerDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return SqlServerDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(SqlServerDataSourceConfig.SQLSERVER_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new SqlServerDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java
new file mode 100644
index 00000000..ec157d39
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-sqlserver/src/main/java/org/apache/seatunnel/datasource/plugin/sqlserver/jdbc/SqlServerOptionRule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.seatunnel.datasource.plugin.sqlserver.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class SqlServerOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:" + "jdbc:sqlserver://localhost:1433;database=xx");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.SQL_SERVER)
+            .withDescription("driver");
+
+    public enum DriverType {
+        SQL_SERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml
new file mode 100644
index 00000000..76a5885f
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-starrocks</artifactId>
+
+    <properties>
+        <mysql-connector.version>8.0.28</mysql-connector.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+
+        <!-- driver -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql-connector.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java
new file mode 100644
index 00000000..427657cc
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksDataSourceConfig.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seatunnel.datasource.plugin.starrocks.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class StarRocksDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-StarRocks";
+
+    public static final DataSourcePluginInfo STAR_ROCKS_DATA_SOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> STAR_ROCKS_SYSTEM_DATABASES =
+        Sets.newHashSet("_statistics_", "information_schema");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(StarRocksOptionRule.URL, StarRocksOptionRule.DRIVER)
+            .optional(StarRocksOptionRule.USER, StarRocksOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder()
+            .required(StarRocksOptionRule.DATABASE, StarRocksOptionRule.TABLE)
+            .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java
new file mode 100644
index 00000000..3f429a7c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceChannel.java
@@ -0,0 +1,175 @@
+/*
+ * 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.seatunnel.datasource.plugin.starrocks.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class StarRocksJdbcDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return StarRocksDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return StarRocksDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(database, null, null, new String[]{"TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName)
+                    && !StarRocksDataSourceConfig.STAR_ROCKS_SYSTEM_DATABASES.contains(
+                    dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (Exception ex) {
+            throw new DataSourcePluginException("get databases failed", ex);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null);) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return null;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(StarRocksOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(StarRocksOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(StarRocksOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(StarRocksOptionRule.USER.key())) {
+            String username = requestParams.get(StarRocksOptionRule.USER.key());
+            String password = requestParams.get(StarRocksOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java
new file mode 100644
index 00000000..65620d0e
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksJdbcDataSourceFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.seatunnel.datasource.plugin.starrocks.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class StarRocksJdbcDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return StarRocksDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(StarRocksDataSourceConfig.STAR_ROCKS_DATA_SOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new StarRocksJdbcDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java
new file mode 100644
index 00000000..d67c2a30
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-starrocks/src/main/java/org/apache/seatunnel/datasource/plugin/starrocks/jdbc/StarRocksOptionRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seatunnel.datasource.plugin.starrocks.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class StarRocksOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + "jdbc:mysql://localhost:9030/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.MYSQL)
+            .withDescription("driver");
+
+    public enum DriverType {
+        MYSQL("com.mysql.cj.jdbc.Driver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml
new file mode 100644
index 00000000..5797396d
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-jdbc-tidb</artifactId>
+
+    <properties />
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-jdbc-mysql</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java
new file mode 100644
index 00000000..f02caff2
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbDataSourceConfig.java
@@ -0,0 +1,51 @@
+/*
+ * 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.seatunnel.datasource.plugin.tidb.jdbc;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class TidbDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "JDBC-TiDB";
+
+    public static final DataSourcePluginInfo TIDB_DATASOURCE_PLUGIN_INFO =
+        DataSourcePluginInfo.builder()
+            .name(PLUGIN_NAME)
+            .icon(PLUGIN_NAME)
+            .version("1.0.0")
+            .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+            .build();
+
+    public static final Set<String> TIDB_SYSTEM_DATABASES =
+        Sets.newHashSet("information_schema", "mysql", "performance_schema", "metrics_schema");
+
+    public static final OptionRule OPTION_RULE =
+        OptionRule.builder()
+            .required(TidbOptionRule.URL, TidbOptionRule.DRIVER)
+            .optional(TidbOptionRule.USER, TidbOptionRule.PASSWORD)
+            .build();
+
+    public static final OptionRule METADATA_RULE =
+        OptionRule.builder().required(TidbOptionRule.DATABASE, TidbOptionRule.TABLE).build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java
new file mode 100644
index 00000000..8558f13c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceChannel.java
@@ -0,0 +1,182 @@
+/*
+ * 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.seatunnel.datasource.plugin.tidb.jdbc;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class TidbJdbcDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return TidbDataSourceConfig.OPTION_RULE;
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return TidbDataSourceConfig.METADATA_RULE;
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(database, null, null, new String[]{"TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName)
+                    && !TidbDataSourceConfig.TIDB_SYSTEM_DATABASES.contains(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (SQLException | ClassNotFoundException e) {
+            throw new DataSourcePluginException("Get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (Connection ignored = getConnection(requestParams)) {
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException("check jdbc connectivity failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = getConnection(requestParams, database)) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, database, table);
+            try (ResultSet resultSet = metaData.getColumns(database, null, table, null)) {
+                while (resultSet.next()) {
+                    TableField tableField = new TableField();
+                    String columnName = resultSet.getString("COLUMN_NAME");
+                    tableField.setPrimaryKey(false);
+                    if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                        tableField.setPrimaryKey(true);
+                    }
+                    tableField.setName(columnName);
+                    tableField.setType(resultSet.getString("TYPE_NAME"));
+                    tableField.setComment(resultSet.getString("REMARKS"));
+                    Object nullable = resultSet.getObject("IS_NULLABLE");
+                    tableField.setNullable(Boolean.TRUE.toString().equals(nullable.toString()));
+                    tableFields.add(tableField);
+                }
+            }
+        } catch (ClassNotFoundException | SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        return tables.parallelStream()
+            .collect(
+                Collectors.toMap(
+                    Function.identity(),
+                    table ->
+                        getTableFields(
+                            pluginName, requestParams, database, table)));
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private Connection getConnection(Map<String, String> requestParams)
+        throws SQLException, ClassNotFoundException {
+        return getConnection(requestParams, null);
+    }
+
+    private Connection getConnection(Map<String, String> requestParams, String databaseName)
+        throws SQLException, ClassNotFoundException {
+        checkNotNull(requestParams.get(TidbOptionRule.DRIVER.key()));
+        checkNotNull(requestParams.get(TidbOptionRule.URL.key()), "Jdbc url cannot be null");
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(TidbOptionRule.URL.key()), databaseName);
+        if (requestParams.containsKey(TidbOptionRule.USER.key())) {
+            String username = requestParams.get(TidbOptionRule.USER.key());
+            String password = requestParams.get(TidbOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java
new file mode 100644
index 00000000..49906bc9
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbJdbcDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.tidb.jdbc;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Set;
+
+@Slf4j
+@AutoService(DataSourceFactory.class)
+public class TidbJdbcDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return TidbDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(TidbDataSourceConfig.TIDB_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new TidbJdbcDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java
new file mode 100644
index 00000000..90af0f95
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-jdbc-tidb/src/main/java/org/apache/seatunnel/datasource/plugin/tidb/jdbc/TidbOptionRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seatunnel.datasource.plugin.tidb.jdbc;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+
+public class TidbOptionRule {
+
+    public static final Option<String> URL =
+        Options.key("url")
+            .stringType()
+            .noDefaultValue()
+            .withDescription(
+                "jdbc url, eg:"
+                    + " jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> USER =
+        Options.key("user").stringType().noDefaultValue().withDescription("jdbc user");
+
+    public static final Option<String> PASSWORD =
+        Options.key("password").stringType().noDefaultValue().withDescription("jdbc password");
+
+    public static final Option<String> DATABASE =
+        Options.key("database").stringType().noDefaultValue().withDescription("jdbc database");
+
+    public static final Option<String> TABLE =
+        Options.key("table").stringType().noDefaultValue().withDescription("jdbc table");
+
+    public static final Option<DriverType> DRIVER =
+        Options.key("driver")
+            .enumType(DriverType.class)
+            .defaultValue(DriverType.MYSQL)
+            .withDescription("driver");
+
+    public enum DriverType {
+        MYSQL("com.mysql.cj.jdbc.Driver"),
+        ;
+        private final String driverClassName;
+
+        DriverType(String driverClassName) {
+            this.driverClassName = driverClassName;
+        }
+
+        public String getDriverClassName() {
+            return driverClassName;
+        }
+
+        @Override
+        public String toString() {
+            return driverClassName;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml
new file mode 100644
index 00000000..64a90557
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-kafka</artifactId>
+
+    <properties>
+        <kafka.client.version>3.2.0</kafka.client.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.kafka</groupId>
+            <artifactId>kafka-clients</artifactId>
+            <version>${kafka.client.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java
new file mode 100644
index 00000000..72e67995
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannel.java
@@ -0,0 +1,115 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.kafka.clients.admin.AdminClient;
+import org.apache.kafka.clients.admin.DescribeClusterOptions;
+import org.apache.kafka.clients.admin.DescribeClusterResult;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Slf4j
+public class KafkaDataSourceChannel implements DataSourceChannel {
+
+    private static final String DATABASE = "default";
+    private static final DescribeClusterOptions DEFAULT_TIMEOUT_OPTIONS =
+        new DescribeClusterOptions().timeoutMs(60 * 1000);
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return KafkaOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return KafkaOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default");
+        try (AdminClient adminClient = createAdminClient(requestParams)) {
+            Set<String> strings = adminClient.listTopics().names().get();
+            return new ArrayList<>(strings);
+        } catch (Exception ex) {
+            throw new DataSourcePluginException(
+                "check kafka connectivity failed, " + ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        return DEFAULT_DATABASES;
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try (AdminClient adminClient = createAdminClient(requestParams)) {
+            // just test the connection
+            DescribeClusterResult describeClusterResult =
+                adminClient.describeCluster(DEFAULT_TIMEOUT_OPTIONS);
+            return CollectionUtils.isNotEmpty(describeClusterResult.nodes().get());
+        } catch (Exception ex) {
+            throw new DataSourcePluginException(
+                "check kafka connectivity failed, " + ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default");
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be default");
+        return Collections.emptyMap();
+    }
+
+    private AdminClient createAdminClient(Map<String, String> requestParams) {
+        return AdminClient.create(
+            KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams));
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java
new file mode 100644
index 00000000..bcba81c0
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class KafkaDataSourceFactory implements DataSourceFactory {
+
+    public static final String KAFKA_PLUGIN_NAME = "Kafka";
+    public static final String KAFKA_PLUGIN_ICON = "kafka";
+    public static final String KAFKA_PLUGIN_VERSION = "1.0.0";
+
+    @Override
+    public String factoryIdentifier() {
+        return KAFKA_PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Sets.newHashSet(
+            DataSourcePluginInfo.builder()
+                .name(KAFKA_PLUGIN_NAME)
+                .icon(KAFKA_PLUGIN_ICON)
+                .version(KAFKA_PLUGIN_VERSION)
+                .supportVirtualTables(true)
+                .type(DatasourcePluginTypeEnum.NO_STRUCTURED.getCode())
+                .build());
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new KafkaDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java
new file mode 100644
index 00000000..b599bb88
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaOptionRule.java
@@ -0,0 +1,67 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+import java.util.Map;
+
+public class KafkaOptionRule {
+
+    public static final Option<String> BOOTSTRAP_SERVERS =
+            Options.key("bootstrap.servers")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Kafka cluster address, separated by \",\".");
+    public static final Option<String> TOPIC =
+            Options.key("topic")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "Kafka topic name. If there are multiple topics, use , to split, for example: \"tpc1,tpc2\".");
+
+    public static final Option<Boolean> PATTERN =
+            Options.key("pattern")
+                    .booleanType()
+                    .defaultValue(false)
+                    .withDescription(
+                            "If pattern is set to true,the regular expression for a pattern of topic names to read from."
+                                    + " All topics in clients with names that match the specified regular expression will be subscribed by the consumer.");
+
+    public static final Option<Map<String, String>> KAFKA_CONFIG =
+            Options.key("kafka.config")
+                    .mapType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "{\n"
+                                    + "client.id=client_1\n"
+                                    + "max.poll.records=500\n"
+                                    + "auto.offset.reset=earliest\n"
+                                    + "enable.auto.commit=false\n"
+                                    + "}");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder().required(BOOTSTRAP_SERVERS).optional(KAFKA_CONFIG).build();
+    }
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder().required(TOPIC).optional(PATTERN).build();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java
new file mode 100644
index 00000000..95988e76
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/main/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import org.apache.seatunnel.shade.com.typesafe.config.Config;
+import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory;
+
+import org.apache.kafka.clients.admin.AdminClientConfig;
+
+import java.util.Map;
+import java.util.Properties;
+
+public class KafkaRequestParamsUtils {
+
+    public static Properties parsePropertiesFromRequestParams(Map<String, String> requestParams) {
+        checkArgument(
+            requestParams.containsKey(KafkaOptionRule.BOOTSTRAP_SERVERS.key()),
+            String.format(
+                "Missing %s in requestParams", KafkaOptionRule.BOOTSTRAP_SERVERS.key()));
+        final Properties properties = new Properties();
+        properties.put(
+            AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
+            requestParams.get(KafkaOptionRule.BOOTSTRAP_SERVERS.key()));
+        if (requestParams.containsKey(KafkaOptionRule.KAFKA_CONFIG.key())) {
+            Config configObject =
+                ConfigFactory.parseString(
+                    requestParams.get(KafkaOptionRule.KAFKA_CONFIG.key()));
+            configObject
+                .entrySet()
+                .forEach(
+                    entry -> {
+                        properties.put(
+                            entry.getKey(), entry.getValue().unwrapped().toString());
+                    });
+        }
+        return properties;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java
new file mode 100644
index 00000000..e694de45
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaDataSourceChannelTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+// todo: use test container to test
+@Slf4j
+@Disabled
+public class KafkaDataSourceChannelTest {
+
+    private static final KafkaDataSourceChannel KAFKA_DATA_SOURCE_CHANNEL =
+        new KafkaDataSourceChannel();
+
+    private static final String KAFKA_PLUGIN_NAME = "kafka";
+    private static final String BOOTSTRAP_SERVER = "localhost:9092";
+
+    private static final Map<String, String> REQUEST_PARAMS =
+        new ImmutableMap.Builder<String, String>()
+            .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), BOOTSTRAP_SERVER)
+            .build();
+
+    @Test
+    public void getDataSourceOptions() {
+        OptionRule dataSourceMetadataFieldsByDataSourceName =
+            KAFKA_DATA_SOURCE_CHANNEL.getDataSourceOptions(KAFKA_PLUGIN_NAME);
+        Assertions.assertEquals(
+            1, dataSourceMetadataFieldsByDataSourceName.getRequiredOptions().size());
+    }
+
+    @Test
+    public void getDatasourceMetadataFieldsByDataSourceName() {
+        OptionRule datasourceMetadataFieldsByDataSourceName =
+            KAFKA_DATA_SOURCE_CHANNEL.getDatasourceMetadataFieldsByDataSourceName(
+                KAFKA_PLUGIN_NAME);
+        Assertions.assertEquals(
+            2, datasourceMetadataFieldsByDataSourceName.getOptionalOptions().size());
+    }
+
+    @Test
+    public void getTables() {
+        List<String> tables =
+            KAFKA_DATA_SOURCE_CHANNEL.getTables(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, null);
+        log.info("{}", tables);
+        Assertions.assertNotNull(tables);
+    }
+
+    @Test
+    public void getDatabases() {
+        List<String> databases =
+            KAFKA_DATA_SOURCE_CHANNEL.getDatabases(KAFKA_PLUGIN_NAME, REQUEST_PARAMS);
+        log.info("{}", databases);
+        Assertions.assertNotNull(databases);
+    }
+
+    @Test
+    public void checkDataSourceConnectivity() {
+        boolean dataSourceConnectivity =
+            KAFKA_DATA_SOURCE_CHANNEL.checkDataSourceConnectivity(
+                KAFKA_PLUGIN_NAME, REQUEST_PARAMS);
+        Assertions.assertTrue(dataSourceConnectivity);
+    }
+
+    @Test
+    public void getTableFields() {
+        List<TableField> tableFields =
+            KAFKA_DATA_SOURCE_CHANNEL.getTableFields(KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", "");
+        log.info("{}", tableFields);
+        Assertions.assertTrue(tableFields.isEmpty());
+    }
+
+    @Test
+    public void testGetTableFields() {
+        Map<String, List<TableField>> tableFields =
+            KAFKA_DATA_SOURCE_CHANNEL.getTableFields(
+                KAFKA_PLUGIN_NAME, REQUEST_PARAMS, "", Collections.emptyList());
+        log.info("{}", tableFields);
+        Assertions.assertTrue(tableFields.isEmpty());
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java
new file mode 100644
index 00000000..67d8d33f
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-kafka/src/test/java/org/apache/seatunnel/datasource/plugin/kafka/KafkaRequestParamsUtilsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.seatunnel.datasource.plugin.kafka;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+class KafkaRequestParamsUtilsTest {
+
+    @Test
+    void parsePropertiesFromRequestParams() {
+        Map<String, String> requestParams =
+            new ImmutableMap.Builder<String, String>()
+                .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092")
+                .put(
+                    KafkaOptionRule.KAFKA_CONFIG.key(),
+                    "{" + "security.protocol = SASL_PLAINTEXT" + "}")
+                .build();
+        Properties properties =
+            KafkaRequestParamsUtils.parsePropertiesFromRequestParams(requestParams);
+        Assertions.assertEquals("SASL_PLAINTEXT", properties.getProperty("security.protocol"));
+    }
+
+    @Test
+    void parsePropertiesFromRequestParamsBadCase() {
+        Assertions.assertDoesNotThrow(
+            () ->
+                KafkaRequestParamsUtils.parsePropertiesFromRequestParams(
+                    new ImmutableMap.Builder<String, String>()
+                        .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092")
+                        .put(KafkaOptionRule.KAFKA_CONFIG.key(), "{}")
+                        .build()));
+
+        Assertions.assertThrows(
+            IllegalArgumentException.class,
+            () -> KafkaRequestParamsUtils.parsePropertiesFromRequestParams(new HashMap<>()));
+
+        Assertions.assertDoesNotThrow(
+            () ->
+                KafkaRequestParamsUtils.parsePropertiesFromRequestParams(
+                    new ImmutableMap.Builder<String, String>()
+                        .put(KafkaOptionRule.BOOTSTRAP_SERVERS.key(), "localhost:9092")
+                        .put(KafkaOptionRule.KAFKA_CONFIG.key(), "")
+                        .build()));
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml
new file mode 100644
index 00000000..c7d565f8
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-mysql-cdc</artifactId>
+
+    <properties>
+        <mysql.version>8.0.16</mysql.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java
new file mode 100644
index 00000000..07a9f385
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceChannel.java
@@ -0,0 +1,249 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.mysql;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import com.google.common.collect.Sets;
+import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class MysqlCDCDataSourceChannel implements DataSourceChannel {
+
+    public static final Set<String> MYSQL_SYSTEM_DATABASES =
+        Sets.newHashSet("information_schema", "mysql", "performance_schema", "sys");
+
+    @Override
+    public boolean canAbleGetSchema() {
+        return true;
+    }
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return MysqlCDCOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return MysqlCDCOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        String pluginName, Map<String, String> requestParams, String database) {
+        return this.getTableNames(requestParams, database);
+    }
+
+    @Override
+    public List<String> getDatabases(String pluginName, Map<String, String> requestParams) {
+        try {
+            return this.getDataBaseNames(requestParams);
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        String pluginName, Map<String, String> requestParams) {
+        return this.checkJdbcConnectivity(requestParams);
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        String pluginName, Map<String, String> requestParams, String database, String table) {
+        return getTableFields(requestParams, database, table);
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String database,
+        List<String> tables) {
+        Map<String, List<TableField>> tableFields = new HashMap<>(tables.size());
+        for (String table : tables) {
+            tableFields.put(table, getTableFields(requestParams, database, table));
+        }
+        return tableFields;
+    }
+
+    @SuppressWarnings("checkstyle:MagicNumber")
+    protected boolean checkJdbcConnectivity(Map<String, String> requestParams) {
+        try (Connection connection = init(requestParams);
+             Statement statement = connection.createStatement()) {
+
+            try (ResultSet resultSet = statement.executeQuery("SHOW MASTER STATUS");) {
+                if (resultSet.next()) {
+                    String binlogFile = resultSet.getString("File");
+                    if (StringUtils.isBlank(binlogFile)) {
+                        throw new DataSourcePluginException("binlog must be enabled");
+                    }
+                } else {
+                    throw new DataSourcePluginException("binlog must be enabled");
+                }
+            }
+
+            try (ResultSet resultSet =
+                     statement.executeQuery("SHOW VARIABLES LIKE 'binlog_format'")) {
+                if (resultSet.next()) {
+                    String binlogFormat = resultSet.getString("Value");
+                    if (!"ROW".equalsIgnoreCase(binlogFormat)) {
+                        throw new DataSourcePluginException("binlog_format must be ROW");
+                    }
+                } else {
+                    throw new DataSourcePluginException("binlog_format must be ROW");
+                }
+            }
+
+            try (ResultSet resultSet =
+                     statement.executeQuery("SHOW VARIABLES LIKE 'binlog_row_image'")) {
+                if (resultSet.next()) {
+                    String binlogRowImage = resultSet.getString("Value");
+                    if (!"FULL".equalsIgnoreCase(binlogRowImage)) {
+                        throw new DataSourcePluginException("binlog_row_image must be FULL");
+                    }
+                } else {
+                    throw new DataSourcePluginException("binlog_row_image must be FULL");
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            throw new DataSourcePluginException(
+                "check jdbc connectivity failed, " + e.getMessage(), e);
+        }
+    }
+
+    protected Connection init(Map<String, String> requestParams) throws SQLException {
+        if (null == requestParams.get(MysqlCDCOptionRule.BASE_URL.key())) {
+            throw new DataSourcePluginException("Jdbc url is null");
+        }
+        String url = requestParams.get(MysqlCDCOptionRule.BASE_URL.key());
+        if (null != requestParams.get(MysqlCDCOptionRule.PASSWORD.key())
+            && null != requestParams.get(MysqlCDCOptionRule.USERNAME.key())) {
+            String username = requestParams.get(MysqlCDCOptionRule.USERNAME.key());
+            String password = requestParams.get(MysqlCDCOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+
+    protected List<String> getDataBaseNames(Map<String, String> requestParams) throws SQLException {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SHOW DATABASES;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("database");
+                if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        }
+    }
+
+    protected List<String> getTableNames(Map<String, String> requestParams, String dbName) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);
+             ResultSet resultSet =
+                 connection
+                     .getMetaData()
+                     .getTables(dbName, null, null, new String[]{"TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    protected List<TableField> getTableFields(
+        Map<String, String> requestParams, String dbName, String tableName) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = init(requestParams);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, dbName, tableName);
+            ResultSet resultSet = metaData.getColumns(dbName, null, tableName, null);
+            while (resultSet.next()) {
+                TableField tableField = new TableField();
+                String columnName = resultSet.getString("COLUMN_NAME");
+                tableField.setPrimaryKey(false);
+                if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                    tableField.setPrimaryKey(true);
+                }
+                tableField.setName(columnName);
+                tableField.setType(resultSet.getString("TYPE_NAME"));
+                tableField.setComment(resultSet.getString("REMARKS"));
+                Object nullable = resultSet.getObject("IS_NULLABLE");
+                boolean isNullable = convertToBoolean(nullable);
+                tableField.setNullable(isNullable);
+                tableFields.add(tableField);
+            }
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private boolean isNotSystemDatabase(String dbName) {
+        return MYSQL_SYSTEM_DATABASES.stream()
+            .noneMatch(
+                systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName));
+    }
+
+    private boolean convertToBoolean(Object value) {
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        if (value instanceof String) {
+            return value.equals("TRUE");
+        }
+        return false;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java
new file mode 100644
index 00000000..b81d4359
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceConfig.java
@@ -0,0 +1,34 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.mysql;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+public class MysqlCDCDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "MySQL-CDC";
+
+    public static final DataSourcePluginInfo MYSQL_CDC_DATASOURCE_PLUGIN_INFO =
+            DataSourcePluginInfo.builder()
+                    .name(PLUGIN_NAME)
+                    .icon(PLUGIN_NAME)
+                    .version("1.0")
+                    .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+                    .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java
new file mode 100644
index 00000000..ac4db000
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCDataSourceFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.mysql;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+
+import java.util.Collections;
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class MysqlCDCDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return "MySQL-CDC";
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Collections.singleton(MysqlCDCDataSourceConfig.MYSQL_CDC_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new MysqlCDCDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java
new file mode 100644
index 00000000..c0104c25
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-mysql-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/mysql/MysqlCDCOptionRule.java
@@ -0,0 +1,74 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.mysql;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+public class MysqlCDCOptionRule {
+
+    public static final Option<String> BASE_URL =
+            Options.key("base-url")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "URL has to be without database, like \"jdbc:mysql://localhost:5432/\" or"
+                                    + "\"jdbc:mysql://localhost:5432\" rather than \"jdbc:mysql://localhost:5432/db\"");
+
+    public static final Option<String> USERNAME =
+            Options.key("username")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "Name of the database to use when connecting to the database server.");
+
+    public static final Option<String> PASSWORD =
+            Options.key("password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Password to use when connecting to the database server.");
+
+    public static final Option<String> DATABASE_NAME =
+            Options.key("database-name")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Database name of the database to monitor.");
+
+    public static final Option<String> TABLE_NAME =
+            Options.key("table-name")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Table name of the database to monitor.");
+    public static final Option<String> SERVER_TIME_ZONE =
+            Options.key("server-time-zone")
+                    .stringType()
+                    .defaultValue("UTC")
+                    .withDescription("The session time zone in database server.");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder()
+                .required(USERNAME, PASSWORD, BASE_URL)
+                .optional(SERVER_TIME_ZONE)
+                .build();
+    }
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder().required(DATABASE_NAME, TABLE_NAME).build();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml
new file mode 100644
index 00000000..7bdd419d
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-plugins-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java
new file mode 100644
index 00000000..dade4457
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceChannel.java
@@ -0,0 +1,83 @@
+/*
+ * 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.seatunnel.datasource.plugin.api;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import com.google.common.collect.ImmutableList;
+import lombok.NonNull;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DataSourceChannel {
+
+    List<String> DEFAULT_DATABASES = ImmutableList.of("default");
+
+    /**
+     * get datasource metadata fields by datasource name
+     *
+     * @param pluginName plugin name
+     * @return datasource metadata fields
+     */
+    OptionRule getDataSourceOptions(@NonNull String pluginName);
+
+    /**
+     * get datasource metadata fields by datasource name
+     *
+     * @param pluginName plugin name
+     * @return datasource metadata fields
+     */
+    OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName);
+
+    List<String> getTables(
+            @NonNull String pluginName, Map<String, String> requestParams, String database);
+
+    List<String> getDatabases(
+            @NonNull String pluginName, @NonNull Map<String, String> requestParams);
+
+    boolean checkDataSourceConnectivity(
+            @NonNull String pluginName, @NonNull Map<String, String> requestParams);
+
+    default boolean canAbleGetSchema() {
+        return false;
+    }
+
+    List<TableField> getTableFields(
+            @NonNull String pluginName,
+            @NonNull Map<String, String> requestParams,
+            @NonNull String database,
+            @NonNull String table);
+
+    Map<String, List<TableField>> getTableFields(
+            @NonNull String pluginName,
+            @NonNull Map<String, String> requestParams,
+            @NonNull String database,
+            @NonNull List<String> tables);
+
+    /**
+     * just check metadata field is right and used by virtual table
+     *
+     * @param requestParams request param(connector params)
+     * @return true if right
+     */
+    default Boolean checkMetadataFieldIsRight(Map<String, String> requestParams) {
+        return true;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java
new file mode 100644
index 00000000..966bb033
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourceFactory.java
@@ -0,0 +1,29 @@
+/*
+ * 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.seatunnel.datasource.plugin.api;
+
+import java.util.Set;
+
+public interface DataSourceFactory {
+
+    String factoryIdentifier();
+
+    Set<DataSourcePluginInfo> supportedDataSources();
+
+    DataSourceChannel createChannel();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java
new file mode 100644
index 00000000..8913c4a4
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.seatunnel.datasource.plugin.api;
+
+public class DataSourcePluginException extends RuntimeException {
+
+    public DataSourcePluginException(String message) {
+        super(message);
+    }
+
+    public DataSourcePluginException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public DataSourcePluginException(Throwable cause) {
+        super(cause);
+    }
+
+    public DataSourcePluginException() {
+        super();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java
new file mode 100644
index 00000000..d496945d
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DataSourcePluginInfo.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seatunnel.datasource.plugin.api;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class DataSourcePluginInfo {
+
+    public String name;
+
+    public String icon;
+
+    public String version;
+
+    /**
+     * @see DatasourcePluginTypeEnum
+     */
+    private Integer type;
+
+    /**
+     * whether support virtual tables, default false
+     */
+    private Boolean supportVirtualTables;
+
+    public DataSourcePluginInfo(
+        String name, String icon, String version, Integer type, Boolean supportVirtualTables) {
+        this.name = checkNotNull(name, "name can not be null");
+        this.icon = checkNotNull(icon, "icon can not be null");
+        this.version = checkNotNull(version, "version can not be null");
+        this.type = checkNotNull(type, "type can not be null");
+        this.supportVirtualTables = supportVirtualTables != null && supportVirtualTables;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java
new file mode 100644
index 00000000..e5d8e004
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/DatasourcePluginTypeEnum.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seatunnel.datasource.plugin.api;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@SuppressWarnings("checkstyle:RegexpSingleline")
+public enum DatasourcePluginTypeEnum {
+    DATABASE(1, "database", "传统数据库"),
+    FILE(2, "file", "文件"),
+    NO_STRUCTURED(3, "no_structured", "非结构化数据(NoSQLs)"),
+    STORAGE(4, "storage", "存储"),
+    REMOTE_CONNECTION(5, "remote_connection", "远程连接");
+
+    private final Integer code;
+
+    private final String name;
+
+    private final String chineseName;
+
+    DatasourcePluginTypeEnum(Integer code, String name, String chineseName) {
+        this.code = checkNotNull(code);
+        this.name = checkNotNull(name);
+        this.chineseName = checkNotNull(chineseName);
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getChineseName() {
+        return chineseName;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java
new file mode 100644
index 00000000..2a27a568
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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.seatunnel.datasource.plugin.api.common;
+
+import org.apache.seatunnel.common.utils.JsonUtils;
+
+import java.util.Map;
+
+public class ParamtersUtils {
+    /**
+     * for some parameters, we need to convert them to {@link Map} eg: s3Options {
+     * "access.value": "org.apache.hadoop.fs.s3a.S3AFileSystem", "access.key":
+     * "AKIAIOSFODNN7EXAMPLE", "hadoop_s3_properties": " fs.s3a.impl =
+     * org.apache.hadoop.fs.s3a.S3AFileSystem fs.s3a.access.key = AKIAIOSFODNN7EXAMPLE "
+     *
+     * <p>Convert parameters to {@link Map}
+     *
+     * @param parameters parameters {@link Map}
+     * @return {@link Map}
+     */
+    public static Map<String, Object> convertParams(Map<String, String> parameters) {
+        String json = JsonUtils.toJsonString(parameters);
+        return JsonUtils.toMap(json, String.class, Object.class);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java
new file mode 100644
index 00000000..fabcaf47
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/model/TableField.java
@@ -0,0 +1,40 @@
+/*
+ * 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.seatunnel.datasource.plugin.api.model;
+
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+public class TableField {
+
+    private String type;
+
+    private String name;
+
+    private String comment;
+
+    private Boolean primaryKey;
+
+    private String defaultValue;
+
+    private Boolean nullable;
+
+    private Map<String, String> properties;
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java
new file mode 100644
index 00000000..8e7cb3d8
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/main/java/org/apache/seatunnel/datasource/plugin/api/utils/JdbcUtils.java
@@ -0,0 +1,43 @@
+/*
+ * 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.seatunnel.datasource.plugin.api.utils;
+
+public class JdbcUtils {
+
+    public static String replaceDatabase(String jdbcUrl, String databaseName) {
+        if (databaseName == null) {
+            return jdbcUrl;
+        }
+        String[] split = jdbcUrl.split("\\?");
+        if (split.length == 1) {
+            return replaceDatabaseWithoutParameter(jdbcUrl, databaseName);
+        }
+        return replaceDatabaseWithoutParameter(split[0], databaseName) + "?" + split[1];
+    }
+
+    private static String replaceDatabaseWithoutParameter(String jdbcUrl, String databaseName) {
+        int lastIndex = jdbcUrl.lastIndexOf(':');
+        char[] chars = jdbcUrl.toCharArray();
+        for (int i = lastIndex + 1; i < chars.length; i++) {
+            if (chars[i] == '/') {
+                return jdbcUrl.substring(0, i + 1) + databaseName;
+            }
+        }
+        return jdbcUrl + "/" + databaseName;
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java
new file mode 100644
index 00000000..98b8d7ee
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-plugins-api/src/test/java/org/apache/seatunnel/datasource/plugin/api/common/ParamtersUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.seatunnel.datasource.plugin.api.common;
+
+import org.apache.seatunnel.common.utils.JsonUtils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ParamtersUtilsTest {
+
+    @Test
+    public void testConvertParams() {
+        Map<String, Object> s3Options = new HashMap<>();
+        s3Options.put("access.key", "myaccess");
+        s3Options.put("access.value", "myvalue");
+        Map<String, String> hadoopConfig = new HashMap<>();
+        hadoopConfig.put("fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem");
+        s3Options.put("hadoopConfig", hadoopConfig);
+        String s3OptionsJson = JsonUtils.toJsonString(s3Options);
+        Map<String, String> s3OptionsMap =
+                JsonUtils.toMap(s3OptionsJson, String.class, String.class);
+
+        Map<String, Object> s3OptionsMapConvertResult = ParamtersUtils.convertParams(s3OptionsMap);
+        Assertions.assertEquals(s3OptionsMapConvertResult.get("hadoopConfig"), hadoopConfig);
+        Assertions.assertEquals(
+                s3OptionsMapConvertResult.get("access.key"), s3Options.get("access.key"));
+        Assertions.assertEquals(
+                s3OptionsMapConvertResult.get("access.value"), s3Options.get("access.value"));
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml
new file mode 100644
index 00000000..3f7e0e6e
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-s3-redshift</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-hadoop3-3.1.4-uber</artifactId>
+            <version>${seatunnel-framework.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.avro</groupId>
+                    <artifactId>avro</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.amazon.redshift</groupId>
+            <artifactId>redshift-jdbc42</artifactId>
+            <version>${redshift.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-aws</artifactId>
+            <version>${hadoop-aws.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>jdk.tools</groupId>
+                    <artifactId>jdk.tools</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-bundle</artifactId>
+            <version>${aws-java-sdk-bundle.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.java
new file mode 100644
index 00000000..f3256186
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/HadoopS3AConfiguration.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.seatunnel.datasource.plugin.redshift.s3;
+
+import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@Slf4j
+public class HadoopS3AConfiguration {
+
+    /* S3 constants */
+    private static final String S3A_SCHEMA = "s3a";
+    private static final String HDFS_S3N_IMPL = "org.apache.hadoop.fs.s3native.NativeS3FileSystem";
+    private static final String HDFS_S3A_IMPL = "org.apache.hadoop.fs.s3a.S3AFileSystem";
+    private static final String S3A_PROTOCOL = "s3a";
+    private static final String DEFAULT_PROTOCOL = "s3n";
+    private static final String S3_FORMAT_KEY = "fs.%s.%s";
+    private static final String HDFS_IMPL_KEY = "impl";
+
+    public static Configuration getConfiguration(Map<String, String> s3Options) {
+
+        if (!s3Options.containsKey(S3RedshiftOptionRule.BUCKET.key())) {
+            throw new IllegalArgumentException(
+                "S3Redshift datasource bucket is null, please check your config");
+        }
+        if (!s3Options.containsKey(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key())) {
+            throw new IllegalArgumentException(
+                "S3Redshift datasource endpoint is null, please check your config");
+        }
+        String bucket = s3Options.get(S3RedshiftOptionRule.BUCKET.key());
+
+        String protocol = DEFAULT_PROTOCOL;
+        if (bucket.startsWith(S3A_PROTOCOL)) {
+            protocol = S3A_PROTOCOL;
+        }
+        String fsImpl = protocol.equals(S3A_PROTOCOL) ? HDFS_S3A_IMPL : HDFS_S3N_IMPL;
+        Configuration hadoopConf = new Configuration();
+        hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket);
+        hadoopConf.set(
+            S3RedshiftOptionRule.FS_S3A_ENDPOINT.key(),
+            s3Options.get(S3RedshiftOptionRule.FS_S3A_ENDPOINT.key()));
+        hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl);
+        if (s3Options.containsKey(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key())) {
+            Arrays.stream(
+                    s3Options
+                        .get(S3RedshiftOptionRule.HADOOP_S3_PROPERTIES.key())
+                        .split("\n"))
+                .map(String::trim)
+                .filter(StringUtils::isNotBlank)
+                .forEach(
+                    line -> {
+                        String[] kv = line.split("=");
+                        if (kv.length == 2) {
+                            hadoopConf.set(kv[0].trim(), kv[1].trim());
+                        }
+                    });
+        }
+        if (S3RedshiftOptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider
+            .getProvider()
+            .equals(s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) {
+            hadoopConf.set(
+                S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(),
+                s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()));
+            hadoopConf.set(
+                "fs.s3a.access.key", s3Options.get(S3RedshiftOptionRule.ACCESS_KEY.key()));
+            hadoopConf.set(
+                "fs.s3a.secret.key", s3Options.get(S3RedshiftOptionRule.SECRET_KEY.key()));
+        } else {
+            hadoopConf.set(
+                S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(),
+                s3Options.get(S3RedshiftOptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()));
+        }
+        return hadoopConf;
+    }
+
+    private static String formatKey(String protocol, String key) {
+        return String.format(S3_FORMAT_KEY, protocol, key);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java
new file mode 100644
index 00000000..07aceb32
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceChannel.java
@@ -0,0 +1,268 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.s3;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.api.utils.JdbcUtils;
+
+import com.google.common.collect.Sets;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Slf4j
+public class S3RedshiftDataSourceChannel implements DataSourceChannel {
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return S3RedshiftOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return S3RedshiftOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        return getTableNames(requestParams, database);
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        try {
+            return getDataBaseNames(pluginName, requestParams);
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("Query redshift databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        checkHdfsS3Connection(requestParams);
+        checkJdbcConnection(requestParams);
+        return true;
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        return getTableFields(requestParams, database, table);
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        // not need this method
+        return null;
+    }
+
+    private void checkJdbcConnection(Map<String, String> requestParams) {
+        String jdbcUrl = requestParams.get(S3RedshiftOptionRule.JDBC_URL.key());
+        String username = requestParams.get(S3RedshiftOptionRule.JDBC_USER.key());
+        String password = requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key());
+        if (StringUtils.isBlank(jdbcUrl)) {
+            throw new DataSourcePluginException("Redshift Jdbc url is empty");
+        }
+        if (StringUtils.isBlank(username) && StringUtils.isBlank(password)) {
+            try (Connection ignored = DriverManager.getConnection(jdbcUrl)) {
+                log.info("Redshift jdbc connection is valid");
+                return;
+            } catch (SQLException e) {
+                throw new DataSourcePluginException(
+                    "Check Redshift jdbc connection failed,please check your config", e);
+            }
+        }
+        try (Connection ignored = DriverManager.getConnection(jdbcUrl, username, password)) {
+            log.info("Redshift jdbc connection is valid");
+        } catch (SQLException e) {
+            throw new DataSourcePluginException(
+                "Check Redshift jdbc connection failed,please check your config", e);
+        }
+    }
+
+    private void checkHdfsS3Connection(Map<String, String> requestParams) {
+        Configuration s3Conf = HadoopS3AConfiguration.getConfiguration(requestParams);
+        try (FileSystem fs = FileSystem.get(s3Conf)) {
+            fs.getFileStatus(new org.apache.hadoop.fs.Path("/"));
+        } catch (IOException e) {
+            throw new DataSourcePluginException(
+                "S3 configuration is invalid, please check your config", e);
+        }
+    }
+
+    protected Connection init(Map<String, String> requestParams, String databaseName)
+        throws SQLException {
+        if (null == requestParams.get(S3RedshiftOptionRule.JDBC_URL.key())) {
+            throw new DataSourcePluginException("Jdbc url is null");
+        }
+        String url =
+            JdbcUtils.replaceDatabase(
+                requestParams.get(S3RedshiftOptionRule.JDBC_URL.key()), databaseName);
+        if (null != requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key())
+            && null != requestParams.get(S3RedshiftOptionRule.JDBC_USER.key())) {
+            String username = requestParams.get(S3RedshiftOptionRule.JDBC_USER.key());
+            String password = requestParams.get(S3RedshiftOptionRule.JDBC_PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+
+    protected List<String> getDataBaseNames(String pluginName, Map<String, String> requestParams)
+        throws SQLException {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = init(requestParams, null);
+             PreparedStatement statement =
+                 connection.prepareStatement("select datname from pg_database;");
+             ResultSet re = statement.executeQuery()) {
+            while (re.next()) {
+                String dbName = re.getString("datname");
+                if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+            return dbNames;
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get databases failed", e);
+        }
+    }
+
+    protected List<String> getTableNames(Map<String, String> requestParams, String dbName) {
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = init(requestParams, dbName);) {
+            ResultSet resultSet =
+                connection.getMetaData().getTables(dbName, null, null, new String[]{"TABLE"});
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                if (StringUtils.isNotBlank(tableName)) {
+                    tableNames.add(tableName);
+                }
+            }
+            return tableNames;
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    protected List<TableField> getTableFields(
+        Map<String, String> requestParams, String dbName, String tableName) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = init(requestParams, dbName);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, dbName, tableName);
+            String[] split = tableName.split("\\.");
+            if (split.length != 2) {
+                throw new DataSourcePluginException(
+                    "Postgresql tableName should composed by schemaName.tableName");
+            }
+            ResultSet resultSet = metaData.getColumns(dbName, split[0], split[1], null);
+            while (resultSet.next()) {
+                TableField tableField = new TableField();
+                String columnName = resultSet.getString("COLUMN_NAME");
+                tableField.setPrimaryKey(false);
+                if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                    tableField.setPrimaryKey(true);
+                }
+                tableField.setName(columnName);
+                tableField.setType(resultSet.getString("TYPE_NAME"));
+                tableField.setComment(resultSet.getString("REMARKS"));
+                Object nullable = resultSet.getObject("IS_NULLABLE");
+                boolean isNullable = convertToBoolean(nullable);
+                tableField.setNullable(isNullable);
+                tableFields.add(tableField);
+            }
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    private String getPrimaryKey(DatabaseMetaData metaData, String dbName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, "%", tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    @SuppressWarnings("checkstyle:MagicNumber")
+    private static boolean checkHostConnectable(String host, int port) {
+        try (Socket socket = new Socket()) {
+            socket.connect(new InetSocketAddress(host, port), 1000);
+            return true;
+        } catch (IOException e) {
+
+            throw new DataSourcePluginException("check host connectable failed", e);
+        }
+    }
+
+    private boolean isNotSystemDatabase(String dbName) {
+        return !POSTGRESQL_SYSTEM_DATABASES.contains(dbName.toLowerCase());
+    }
+
+    private boolean convertToBoolean(Object value) {
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        if (value instanceof String) {
+            return value.equals("TRUE");
+        }
+        return false;
+    }
+
+    public static final Set<String> POSTGRESQL_SYSTEM_DATABASES =
+        Sets.newHashSet(
+            "information_schema",
+            "pg_catalog",
+            "root",
+            "pg_toast",
+            "pg_temp_1",
+            "pg_toast_temp_1",
+            "postgres",
+            "template0",
+            "template1");
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java
new file mode 100644
index 00000000..3ef212ac
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftDataSourceFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.s3;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class S3RedshiftDataSourceFactory implements DataSourceFactory {
+    @Override
+    public String factoryIdentifier() {
+        return "S3-Redshift";
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        DataSourcePluginInfo s3DatasourcePluginInfo =
+            DataSourcePluginInfo.builder()
+                .name("S3-Redshift")
+                .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+                .version("1.0.0")
+                .supportVirtualTables(false)
+                .icon("S3-Redshift")
+                .icon("S3-Redshift")
+                .build();
+
+        return Sets.newHashSet(s3DatasourcePluginInfo);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new S3RedshiftDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java
new file mode 100644
index 00000000..5d69ee80
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3-redshift/src/main/java/org/apache/seatunnel/datasource/plugin/redshift/s3/S3RedshiftOptionRule.java
@@ -0,0 +1,191 @@
+/*
+ * 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.seatunnel.datasource.plugin.redshift.s3;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class S3RedshiftOptionRule {
+
+    public static final Option<String> JDBC_URL =
+            Options.key("jdbc_url")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "Redshift jdbc connection url, eg: jdbc:redshift://localhost:5439/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
+
+    public static final Option<String> JDBC_USER =
+            Options.key("jdbc_user")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Redshift jdbc connection user");
+
+    public static final Option<String> JDBC_PASSWORD =
+            Options.key("jdbc_password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Redshift jdbc connection password");
+
+    public static final Option<String> ACCESS_KEY =
+            Options.key("access_key")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 access key");
+
+    public static final Option<String> SECRET_KEY =
+            Options.key("secret_key")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 secret key");
+
+    public static final Option<String> BUCKET =
+            Options.key("bucket").stringType().noDefaultValue().withDescription("S3 bucket name");
+
+    public static final Option<String> FS_S3A_ENDPOINT =
+            Options.key("fs.s3a.endpoint")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("fs s3a endpoint");
+
+    public static final Option<S3aAwsCredentialsProvider> S3A_AWS_CREDENTIALS_PROVIDER =
+            Options.key("fs.s3a.aws.credentials.provider")
+                    .enumType(S3aAwsCredentialsProvider.class)
+                    .defaultValue(S3aAwsCredentialsProvider.InstanceProfileCredentialsProvider)
+                    .withDescription("s3a aws credentials provider");
+
+    public static final Option<Map<String, String>> HADOOP_S3_PROPERTIES =
+            Options.key("hadoop_s3_properties")
+                    .mapType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "{"
+                                    + "fs.s3a.buffer.dir = /data/st_test/s3a\n"
+                                    + "fs.s3a.fast.upload.buffer = disk\n"
+                                    + "}");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder()
+                .required(
+                        JDBC_URL,
+                        BUCKET,
+                        FS_S3A_ENDPOINT,
+                        S3A_AWS_CREDENTIALS_PROVIDER,
+                        JDBC_USER,
+                        JDBC_PASSWORD)
+                .optional(HADOOP_S3_PROPERTIES)
+                .conditional(
+                        S3A_AWS_CREDENTIALS_PROVIDER,
+                        S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider,
+                        ACCESS_KEY,
+                        SECRET_KEY)
+                .build();
+    }
+
+    public static final Option<String> PATH =
+            Options.key("path").stringType().noDefaultValue().withDescription("S3 write path");
+
+    public static final Option<FileFormat> TYPE =
+            Options.key("file_format_type")
+                    .enumType(FileFormat.class)
+                    .noDefaultValue()
+                    .withDescription("S3 write type");
+
+    public static final Option<String> DELIMITER =
+            Options.key("delimiter")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write delimiter");
+
+    public static final Option<Map<String, String>> SCHEMA =
+            Options.key("schema").mapType().noDefaultValue().withDescription("SeaTunnel Schema");
+
+    public static final Option<Boolean> PARSE_PARSE_PARTITION_FROM_PATH =
+            Options.key("parse_partition_from_path")
+                    .booleanType()
+                    .noDefaultValue()
+                    .withDescription("S3 write parse_partition_from_path");
+
+    public static final Option<String> DATE_FORMAT =
+            Options.key("date_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write date_format");
+
+    public static final Option<String> DATETIME_FORMAT =
+            Options.key("time_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write time_format");
+
+    public static final Option<String> TIME_FORMAT =
+            Options.key("datetime_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write datetime_format");
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder()
+                .required(PATH, TYPE)
+                .conditional(TYPE, FileFormat.TEXT, DELIMITER)
+                .conditional(TYPE, Arrays.asList(FileFormat.TEXT, FileFormat.JSON), SCHEMA)
+                .optional(PARSE_PARSE_PARTITION_FROM_PATH)
+                .optional(DATE_FORMAT)
+                .optional(DATETIME_FORMAT)
+                .optional(TIME_FORMAT)
+                .build();
+    }
+
+    public enum S3aAwsCredentialsProvider {
+        SimpleAWSCredentialsProvider("org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"),
+
+        InstanceProfileCredentialsProvider("com.amazonaws.auth.InstanceProfileCredentialsProvider");
+
+        private String provider;
+
+        S3aAwsCredentialsProvider(String provider) {
+            this.provider = provider;
+        }
+
+        public String getProvider() {
+            return provider;
+        }
+
+        @Override
+        public String toString() {
+            return provider;
+        }
+    }
+
+    public enum FileFormat {
+        CSV("csv"),
+        TEXT("txt"),
+        PARQUET("parquet"),
+        ORC("orc"),
+        JSON("json");
+
+        private final String type;
+
+        FileFormat(String type) {
+            this.type = type;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml
new file mode 100644
index 00000000..be117f58
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-s3</artifactId>
+
+    <properties>
+        <hadoop-uber.version>2.4.4-WS-SNAPSHOT</hadoop-uber.version>
+        <hadoop-aws.version>3.1.4</hadoop-aws.version>
+        <aws-java-sdk-bundle.version>1.11.271</aws-java-sdk-bundle.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-hadoop3-3.1.4-uber</artifactId>
+            <version>${hadoop-uber.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.avro</groupId>
+                    <artifactId>avro</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-aws</artifactId>
+            <version>${hadoop-aws.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>jdk.tools</groupId>
+                    <artifactId>jdk.tools</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-bundle</artifactId>
+            <version>${aws-java-sdk-bundle.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java
new file mode 100644
index 00000000..ecce9793
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/HadoopS3AConfiguration.java
@@ -0,0 +1,95 @@
+/*
+ * 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.seatunnel.datasource.plugin.s3;
+
+import static org.apache.hadoop.fs.FileSystem.FS_DEFAULT_NAME_KEY;
+
+import org.apache.seatunnel.shade.com.typesafe.config.Config;
+import org.apache.seatunnel.shade.com.typesafe.config.ConfigFactory;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hadoop.conf.Configuration;
+
+import java.util.Map;
+
+@Slf4j
+public class HadoopS3AConfiguration {
+
+    /* S3 constants */
+    private static final String S3A_SCHEMA = "s3a";
+    private static final String HDFS_S3N_IMPL = "org.apache.hadoop.fs.s3native.NativeS3FileSystem";
+    private static final String HDFS_S3A_IMPL = "org.apache.hadoop.fs.s3a.S3AFileSystem";
+    private static final String S3A_PROTOCOL = "s3a";
+    private static final String DEFAULT_PROTOCOL = "s3n";
+    private static final String S3_FORMAT_KEY = "fs.%s.%s";
+    private static final String HDFS_IMPL_KEY = "impl";
+
+    public static Configuration getConfiguration(Map<String, String> s3Options) {
+
+        if (!s3Options.containsKey(S3OptionRule.BUCKET.key())) {
+            throw new IllegalArgumentException(
+                "S3 datasource bucket is null, please check your config");
+        }
+        if (!s3Options.containsKey(S3OptionRule.FS_S3A_ENDPOINT.key())) {
+            throw new IllegalArgumentException(
+                "S3 datasource endpoint is null, please check your config");
+        }
+        String bucket = s3Options.get(S3OptionRule.BUCKET.key());
+
+        String protocol = DEFAULT_PROTOCOL;
+        if (bucket.startsWith(S3A_PROTOCOL)) {
+            protocol = S3A_PROTOCOL;
+        }
+        String fsImpl = protocol.equals(S3A_PROTOCOL) ? HDFS_S3A_IMPL : HDFS_S3N_IMPL;
+        Configuration hadoopConf = new Configuration();
+        hadoopConf.set(FS_DEFAULT_NAME_KEY, bucket);
+        hadoopConf.set(
+            S3OptionRule.FS_S3A_ENDPOINT.key(),
+            s3Options.get(S3OptionRule.FS_S3A_ENDPOINT.key()));
+        hadoopConf.set(formatKey(protocol, HDFS_IMPL_KEY), fsImpl);
+        if (s3Options.containsKey(S3OptionRule.HADOOP_S3_PROPERTIES.key())) {
+            Config configObject =
+                ConfigFactory.parseString(
+                    s3Options.get(S3OptionRule.HADOOP_S3_PROPERTIES.key()));
+            configObject
+                .entrySet()
+                .forEach(
+                    entry -> {
+                        hadoopConf.set(
+                            entry.getKey(), entry.getValue().unwrapped().toString());
+                    });
+        }
+        if (S3OptionRule.S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider.getProvider()
+            .equals(s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()))) {
+            hadoopConf.set(
+                S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(),
+                s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()));
+            hadoopConf.set("fs.s3a.access.key", s3Options.get(S3OptionRule.ACCESS_KEY.key()));
+            hadoopConf.set("fs.s3a.secret.key", s3Options.get(S3OptionRule.SECRET_KEY.key()));
+        } else {
+            hadoopConf.set(
+                S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key(),
+                s3Options.get(S3OptionRule.S3A_AWS_CREDENTIALS_PROVIDER.key()));
+        }
+        return hadoopConf;
+    }
+
+    private static String formatKey(String protocol, String key) {
+        return String.format(S3_FORMAT_KEY, protocol, key);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java
new file mode 100644
index 00000000..640b6f16
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DataSourceFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.seatunnel.datasource.plugin.s3;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class S3DataSourceFactory implements DataSourceFactory {
+
+    private static final String PLUGIN_NAME = "S3";
+
+    @Override
+    public String factoryIdentifier() {
+        return PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        DataSourcePluginInfo s3DatasourcePluginInfo =
+            DataSourcePluginInfo.builder()
+                .name(PLUGIN_NAME)
+                .type(DatasourcePluginTypeEnum.FILE.getCode())
+                .version("1.0.0")
+                .supportVirtualTables(false)
+                .icon("S3File")
+                .build();
+
+        return Sets.newHashSet(s3DatasourcePluginInfo);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new S3DatasourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java
new file mode 100644
index 00000000..e1ba8de5
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3DatasourceChannel.java
@@ -0,0 +1,89 @@
+/*
+ * 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.seatunnel.datasource.plugin.s3;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import lombok.NonNull;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class S3DatasourceChannel implements DataSourceChannel {
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return S3OptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return S3OptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        @NonNull String pluginName, Map<String, String> requestParams, String database) {
+        throw new UnsupportedOperationException("getTables is not supported for S3 datasource");
+    }
+
+    @Override
+    public List<String> getDatabases(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        throw new UnsupportedOperationException("getDatabases is not supported for S3 datasource");
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
+        Configuration conf = HadoopS3AConfiguration.getConfiguration(requestParams);
+        try (FileSystem fs = FileSystem.get(conf)) {
+            fs.listStatus(new Path("/"));
+            return true;
+        } catch (IOException e) {
+            throw new DataSourcePluginException(
+                String.format("check s3 connectivity failed, config is: %s", requestParams), e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull String table) {
+        throw new UnsupportedOperationException(
+            "getTableFields is not supported for S3 datasource");
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        @NonNull String pluginName,
+        @NonNull Map<String, String> requestParams,
+        @NonNull String database,
+        @NonNull List<String> tables) {
+        throw new UnsupportedOperationException(
+            "getTableFields is not supported for S3 datasource");
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java
new file mode 100644
index 00000000..ba56f303
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-s3/src/main/java/org/apache/seatunnel/datasource/plugin/s3/S3OptionRule.java
@@ -0,0 +1,166 @@
+/*
+ * 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.seatunnel.datasource.plugin.s3;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class S3OptionRule {
+
+    public static final Option<String> ACCESS_KEY =
+            Options.key("access_key")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 access key");
+
+    public static final Option<String> SECRET_KEY =
+            Options.key("secret_key")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 secret key");
+
+    public static final Option<String> BUCKET =
+            Options.key("bucket").stringType().noDefaultValue().withDescription("S3 bucket name");
+
+    public static final Option<String> FS_S3A_ENDPOINT =
+            Options.key("fs.s3a.endpoint")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("fs s3a endpoint");
+
+    public static final Option<S3aAwsCredentialsProvider> S3A_AWS_CREDENTIALS_PROVIDER =
+            Options.key("fs.s3a.aws.credentials.provider")
+                    .enumType(S3aAwsCredentialsProvider.class)
+                    .defaultValue(S3aAwsCredentialsProvider.InstanceProfileCredentialsProvider)
+                    .withDescription("s3a aws credentials provider");
+
+    public static final Option<Map<String, String>> HADOOP_S3_PROPERTIES =
+            Options.key("hadoop_s3_properties")
+                    .mapType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "{\n"
+                                    + "fs.s3a.buffer.dir=/data/st_test/s3a\n"
+                                    + "fs.s3a.fast.upload.buffer=disk\n"
+                                    + "}");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder()
+                .required(BUCKET, FS_S3A_ENDPOINT, S3A_AWS_CREDENTIALS_PROVIDER)
+                .optional(HADOOP_S3_PROPERTIES)
+                .conditional(
+                        S3A_AWS_CREDENTIALS_PROVIDER,
+                        S3aAwsCredentialsProvider.SimpleAWSCredentialsProvider,
+                        ACCESS_KEY,
+                        SECRET_KEY)
+                .build();
+    }
+
+    public static final Option<String> PATH =
+            Options.key("path").stringType().noDefaultValue().withDescription("S3 write path");
+
+    public static final Option<FileFormat> TYPE =
+            Options.key("file_format_type")
+                    .enumType(FileFormat.class)
+                    .noDefaultValue()
+                    .withDescription("S3 write type");
+
+    public static final Option<String> DELIMITER =
+            Options.key("delimiter")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write delimiter");
+
+    public static final Option<Map<String, String>> SCHEMA =
+            Options.key("schema").mapType().noDefaultValue().withDescription("SeaTunnel Schema");
+
+    public static final Option<Boolean> PARSE_PARSE_PARTITION_FROM_PATH =
+            Options.key("parse_partition_from_path")
+                    .booleanType()
+                    .noDefaultValue()
+                    .withDescription("S3 write parse_partition_from_path");
+
+    public static final Option<String> DATE_FORMAT =
+            Options.key("date_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write date_format");
+
+    public static final Option<String> DATETIME_FORMAT =
+            Options.key("time_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write time_format");
+
+    public static final Option<String> TIME_FORMAT =
+            Options.key("datetime_format")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("S3 write datetime_format");
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder()
+                .required(PATH, TYPE)
+                .conditional(TYPE, FileFormat.TEXT, DELIMITER)
+                .conditional(TYPE, Arrays.asList(FileFormat.TEXT, FileFormat.JSON), SCHEMA)
+                .optional(PARSE_PARSE_PARTITION_FROM_PATH)
+                .optional(DATE_FORMAT)
+                .optional(DATETIME_FORMAT)
+                .optional(TIME_FORMAT)
+                .build();
+    }
+
+    public enum S3aAwsCredentialsProvider {
+        SimpleAWSCredentialsProvider("org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"),
+
+        InstanceProfileCredentialsProvider("com.amazonaws.auth.InstanceProfileCredentialsProvider");
+
+        private String provider;
+
+        S3aAwsCredentialsProvider(String provider) {
+            this.provider = provider;
+        }
+
+        public String getProvider() {
+            return provider;
+        }
+
+        @Override
+        public String toString() {
+            return provider;
+        }
+    }
+
+    public enum FileFormat {
+        CSV("csv"),
+        TEXT("txt"),
+        PARQUET("parquet"),
+        ORC("orc"),
+        JSON("json");
+
+        private final String type;
+
+        FileFormat(String type) {
+            this.type = type;
+        }
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml
new file mode 100644
index 00000000..467b7914
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.seatunnel</groupId>
+        <artifactId>seatunnel-datasource-plugins</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>datasource-sqlserver-cdc</artifactId>
+
+    <properties>
+        <sqlserver.version>9.2.1.jre8</sqlserver.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>datasource-plugins-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.seatunnel</groupId>
+            <artifactId>seatunnel-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.auto.service</groupId>
+            <artifactId>auto-service</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+            <version>${sqlserver.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java
new file mode 100644
index 00000000..4fed9948
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceChannel.java
@@ -0,0 +1,231 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.sqlserver;
+
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+
+import com.google.common.collect.Sets;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Slf4j
+public class SqlServerCDCDataSourceChannel implements DataSourceChannel {
+
+    public static final Set<String> MYSQL_SYSTEM_DATABASES =
+        Sets.newHashSet("master", "tempdb", "model", "msdb");
+
+    @Override
+    public boolean canAbleGetSchema() {
+        return true;
+    }
+
+    @Override
+    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
+        return SqlServerCDCOptionRule.optionRule();
+    }
+
+    @Override
+    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
+        return SqlServerCDCOptionRule.metadataRule();
+    }
+
+    @Override
+    public List<String> getTables(
+        String pluginName, Map<String, String> requestParams, String database) {
+        return this.getTableNames(requestParams, database);
+    }
+
+    @Override
+    public List<String> getDatabases(String pluginName, Map<String, String> requestParams) {
+        try {
+            return this.getDataBaseNames(requestParams);
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get databases failed", e);
+        }
+    }
+
+    @Override
+    public boolean checkDataSourceConnectivity(
+        String pluginName, Map<String, String> requestParams) {
+        try (Connection connection = init(requestParams);
+             PreparedStatement statement = connection.prepareStatement("SELECT 1");
+             ResultSet rs = statement.executeQuery()) {
+            return rs.next();
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("connect datasource failed", e);
+        }
+    }
+
+    @Override
+    public List<TableField> getTableFields(
+        String pluginName, Map<String, String> requestParams, String database, String table) {
+        Pair<String, String> pair = parseSchemaAndTable(table);
+        return getTableFields(requestParams, database, pair.getLeft(), pair.getRight());
+    }
+
+    @Override
+    public Map<String, List<TableField>> getTableFields(
+        String pluginName,
+        Map<String, String> requestParams,
+        String database,
+        List<String> tables) {
+        Map<String, List<TableField>> tableFields = new HashMap<>(tables.size());
+        for (String table : tables) {
+            tableFields.put(table, getTableFields(pluginName, requestParams, database, table));
+        }
+        return tableFields;
+    }
+
+    private Connection init(Map<String, String> requestParams) throws SQLException {
+        if (null == requestParams.get(SqlServerCDCOptionRule.BASE_URL.key())) {
+            throw new DataSourcePluginException("Jdbc url is null");
+        }
+        String url = requestParams.get(SqlServerCDCOptionRule.BASE_URL.key());
+        if (null != requestParams.get(SqlServerCDCOptionRule.PASSWORD.key())
+            && null != requestParams.get(SqlServerCDCOptionRule.USERNAME.key())) {
+            String username = requestParams.get(SqlServerCDCOptionRule.USERNAME.key());
+            String password = requestParams.get(SqlServerCDCOptionRule.PASSWORD.key());
+            return DriverManager.getConnection(url, username, password);
+        }
+        return DriverManager.getConnection(url);
+    }
+
+    private List<String> getDataBaseNames(Map<String, String> requestParams) throws SQLException {
+        List<String> dbNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);
+             PreparedStatement statement =
+                 connection.prepareStatement(
+                     "SELECT NAME FROM SYS.DATABASES WHERE IS_CDC_ENABLED = 1;");
+             ResultSet re = statement.executeQuery()) {
+            // filter system databases
+            while (re.next()) {
+                String dbName = re.getString("NAME");
+                if (StringUtils.isNotBlank(dbName) && isNotSystemDatabase(dbName)) {
+                    dbNames.add(dbName);
+                }
+            }
+
+            return dbNames;
+        }
+    }
+
+    private List<String> getTableNames(Map<String, String> requestParams, String dbName) {
+        final String sql =
+            String.format(
+                "SELECT SCHEMAS.NAME AS SCHEMA_NAME, TABLES.NAME AS TABLE_NAME"
+                    + "    FROM %s.SYS.SCHEMAS AS SCHEMAS"
+                    + "        JOIN %s.SYS.TABLES AS TABLES"
+                    + "            ON SCHEMAS.SCHEMA_ID = TABLES.SCHEMA_ID"
+                    + "                   AND TABLES.IS_TRACKED_BY_CDC = 1",
+                dbName, dbName);
+
+        List<String> tableNames = new ArrayList<>();
+        try (Connection connection = init(requestParams);
+             Statement statement = connection.createStatement();
+             ResultSet resultSet = statement.executeQuery(sql)) {
+            while (resultSet.next()) {
+                String schemaName = resultSet.getString("SCHEMA_NAME");
+                String tableName = resultSet.getString("TABLE_NAME");
+                tableNames.add(schemaName + "." + tableName);
+            }
+            return tableNames;
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table names failed", e);
+        }
+    }
+
+    private List<TableField> getTableFields(
+        Map<String, String> requestParams, String dbName, String schemaName, String tableName) {
+        List<TableField> tableFields = new ArrayList<>();
+        try (Connection connection = init(requestParams);) {
+            DatabaseMetaData metaData = connection.getMetaData();
+            String primaryKey = getPrimaryKey(metaData, dbName, schemaName, tableName);
+            ResultSet resultSet = metaData.getColumns(dbName, schemaName, tableName, null);
+            while (resultSet.next()) {
+                TableField tableField = new TableField();
+                String columnName = resultSet.getString("COLUMN_NAME");
+                tableField.setPrimaryKey(false);
+                if (StringUtils.isNotBlank(primaryKey) && primaryKey.equals(columnName)) {
+                    tableField.setPrimaryKey(true);
+                }
+                tableField.setName(columnName);
+                tableField.setType(resultSet.getString("TYPE_NAME"));
+                tableField.setComment(resultSet.getString("REMARKS"));
+                Object nullable = resultSet.getObject("IS_NULLABLE");
+                boolean isNullable = convertToBoolean(nullable);
+                tableField.setNullable(isNullable);
+                tableFields.add(tableField);
+            }
+        } catch (SQLException e) {
+            throw new DataSourcePluginException("get table fields failed", e);
+        }
+        return tableFields;
+    }
+
+    private String getPrimaryKey(
+        DatabaseMetaData metaData, String dbName, String schemaName, String tableName)
+        throws SQLException {
+        ResultSet primaryKeysInfo = metaData.getPrimaryKeys(dbName, schemaName, tableName);
+        while (primaryKeysInfo.next()) {
+            return primaryKeysInfo.getString("COLUMN_NAME");
+        }
+        return null;
+    }
+
+    private boolean isNotSystemDatabase(String dbName) {
+        return MYSQL_SYSTEM_DATABASES.stream()
+            .noneMatch(
+                systemDatabase -> StringUtils.equalsAnyIgnoreCase(systemDatabase, dbName));
+    }
+
+    private boolean convertToBoolean(Object value) {
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        }
+        if (value instanceof String) {
+            return value.equals("TRUE");
+        }
+        return false;
+    }
+
+    private Pair<String, String> parseSchemaAndTable(String tableName) {
+        String[] schemaAndTable = tableName.split("\\.");
+        if (schemaAndTable.length != 2) {
+            throw new DataSourcePluginException("table name is invalid");
+        }
+        return Pair.of(schemaAndTable[0], schemaAndTable[1]);
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java
new file mode 100644
index 00000000..4da5b9d5
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceConfig.java
@@ -0,0 +1,34 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.sqlserver;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.plugin.api.DatasourcePluginTypeEnum;
+
+public class SqlServerCDCDataSourceConfig {
+
+    public static final String PLUGIN_NAME = "SqlServer-CDC";
+
+    public static final DataSourcePluginInfo SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO =
+            DataSourcePluginInfo.builder()
+                    .name(PLUGIN_NAME)
+                    .icon(PLUGIN_NAME)
+                    .version("1.0")
+                    .type(DatasourcePluginTypeEnum.DATABASE.getCode())
+                    .build();
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java
new file mode 100644
index 00000000..321f9c8c
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCDataSourceFactory.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.cdc.sqlserver;
+
+import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.api.DataSourceFactory;
+import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginInfo;
+
+import com.google.auto.service.AutoService;
+
+import java.util.Collections;
+import java.util.Set;
+
+@AutoService(DataSourceFactory.class)
+public class SqlServerCDCDataSourceFactory implements DataSourceFactory {
+
+    @Override
+    public String factoryIdentifier() {
+        return SqlServerCDCDataSourceConfig.PLUGIN_NAME;
+    }
+
+    @Override
+    public Set<DataSourcePluginInfo> supportedDataSources() {
+        return Collections.singleton(
+            SqlServerCDCDataSourceConfig.SQLSERVER_CDC_DATASOURCE_PLUGIN_INFO);
+    }
+
+    @Override
+    public DataSourceChannel createChannel() {
+        return new SqlServerCDCDataSourceChannel();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java
new file mode 100644
index 00000000..95cf360b
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/main/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/SqlServerCDCOptionRule.java
@@ -0,0 +1,73 @@
+/*
+ * 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.seatunnel.datasource.plugin.cdc.sqlserver;
+
+import org.apache.seatunnel.api.configuration.Option;
+import org.apache.seatunnel.api.configuration.Options;
+import org.apache.seatunnel.api.configuration.util.OptionRule;
+
+public class SqlServerCDCOptionRule {
+
+    public static final Option<String> BASE_URL =
+            Options.key("base-url")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "URL has to be without database, like \"jdbc:sqlserver://localhost:1433;databaseName=test/\"");
+
+    public static final Option<String> USERNAME =
+            Options.key("username")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription(
+                            "Name of the database to use when connecting to the database server.");
+
+    public static final Option<String> PASSWORD =
+            Options.key("password")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Password to use when connecting to the database server.");
+
+    public static final Option<String> DATABASE_NAME =
+            Options.key("database-name")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Database name of the database to monitor.");
+
+    public static final Option<String> TABLE_NAME =
+            Options.key("table-name")
+                    .stringType()
+                    .noDefaultValue()
+                    .withDescription("Table name of the database to monitor.");
+    public static final Option<String> SERVER_TIME_ZONE =
+            Options.key("server-time-zone")
+                    .stringType()
+                    .defaultValue("UTC")
+                    .withDescription("The session time zone in database server.");
+
+    public static OptionRule optionRule() {
+        return OptionRule.builder()
+                .required(USERNAME, PASSWORD, BASE_URL)
+                .optional(SERVER_TIME_ZONE)
+                .build();
+    }
+
+    public static OptionRule metadataRule() {
+        return OptionRule.builder().required(DATABASE_NAME, TABLE_NAME).build();
+    }
+}
diff --git a/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java
new file mode 100644
index 00000000..b815a318
--- /dev/null
+++ b/seatunnel-datasource/seatunnel-datasource-plugins/datasource-sqlserver-cdc/src/test/java/org/apache/seatunnel/datasource/plugin/cdc/sqlserver/test/TestSqlServerCDCDataSourceChannel.java
@@ -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.
+ */
+
+package org.apache.seatunnel.datasource.plugin.cdc.sqlserver.test;
+
+import org.apache.seatunnel.datasource.plugin.api.model.TableField;
+import org.apache.seatunnel.datasource.plugin.cdc.sqlserver.SqlServerCDCDataSourceChannel;
+import org.apache.seatunnel.datasource.plugin.cdc.sqlserver.SqlServerCDCDataSourceConfig;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class TestSqlServerCDCDataSourceChannel {
+
+    @Test
+    @Disabled
+    public void testConnect() {
+        SqlServerCDCDataSourceChannel channel = new SqlServerCDCDataSourceChannel();
+        Map<String, String> requestParams = new TreeMap<>();
+        requestParams.put("base-url", "jdbc:sqlserver://localhost:1433;databaseName=test");
... 2902 lines suppressed ...