diff --git a/build.gradle b/build.gradle
index 63e9301a43..3d2f8604a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -210,6 +210,48 @@ tasks.register('dist') {
}
}
+tasks.register('dist-admin') {
+ subprojects.forEach { subProject ->
+ dependsOn("${subProject.path}:jar")
+ }
+ def includedProjects =
+ [
+ "eventmesh-admin-server",
+ "eventmesh-common",
+ "eventmesh-spi",
+ "eventmesh-registry:eventmesh-registry-api",
+ "eventmesh-registry:eventmesh-registry-nacos",
+ "eventmesh-openconnect:eventmesh-openconnect-offsetmgmt-plugin:eventmesh-openconnect-offsetmgmt-api"
+ ]
+ doLast {
+ includedProjects.each {
+ def subProject = findProject(it)
+ copy {
+ from subProject.jar.archivePath
+ into rootProject.file('dist/apps')
+ }
+ copy {
+ from subProject.configurations.runtimeClasspath
+ into rootProject.file('dist/lib')
+ exclude 'eventmesh-*'
+ }
+ copy {
+ from subProject.file('bin')
+ into rootProject.file('dist/bin')
+ }
+ copy {
+ from subProject.file('conf')
+ from subProject.sourceSets.main.resources.srcDirs
+ into rootProject.file('dist/conf')
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ exclude 'META-INF'
+ }
+
+ }
+ }
+
+}
+
tasks.register('installPlugin') {
var pluginProjects = subprojects.findAll {
it.file('gradle.properties').exists()
@@ -754,11 +796,12 @@ subprojects {
dependency "software.amazon.awssdk:s3:2.26.3"
dependency "com.github.rholder:guava-retrying:2.0.0"
- dependency "org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.2"
dependency "com.alibaba:druid-spring-boot-starter:1.2.23"
+ dependency "com.baomidou:mybatis-plus-boot-starter:3.5.5"
dependency "org.springframework.boot:spring-boot-starter-jetty:2.7.18"
- dependency "com.baomidou:mybatis-plus:3.5.7"
dependency "com.mysql:mysql-connector-j:8.4.0"
+ dependency "org.springframework.boot:spring-boot-starter-jetty:2.7.10"
+ dependency "org.locationtech.jts:jts-core:1.19.0"
}
}
}
diff --git a/eventmesh-admin-server/build.gradle b/eventmesh-admin-server/build.gradle
index 6f91f48001..6de881725a 100644
--- a/eventmesh-admin-server/build.gradle
+++ b/eventmesh-admin-server/build.gradle
@@ -31,18 +31,25 @@ dependencies {
implementation "io.grpc:grpc-stub"
implementation "io.grpc:grpc-netty-shaded"
- // https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter
- implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.7'
- implementation "org.reflections:reflections:0.10.2"
+ // https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter
+ implementation "com.baomidou:mybatis-plus-boot-starter"
- // https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter
- implementation "com.alibaba:druid-spring-boot-starter"
- compileOnly 'com.mysql:mysql-connector-j'
- compileOnly 'org.projectlombok:lombok'
- annotationProcessor 'org.projectlombok:lombok'
+ // https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter
+ implementation "com.alibaba:druid-spring-boot-starter"
+ implementation 'com.mysql:mysql-connector-j'
+ compileOnly 'org.projectlombok:lombok'
+ annotationProcessor 'org.projectlombok:lombok'
}
configurations.implementation {
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
}
+sourceSets {
+ main {
+ resources {
+ srcDirs = ['src/main/resources', 'conf']
+ }
+ }
+}
+
diff --git a/eventmesh-admin-server/src/main/resources/application.yaml b/eventmesh-admin-server/conf/application.yaml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/application.yaml
rename to eventmesh-admin-server/conf/application.yaml
diff --git a/eventmesh-admin-server/src/main/resources/eventmesh-admin.properties b/eventmesh-admin-server/conf/eventmesh-admin.properties
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/eventmesh-admin.properties
rename to eventmesh-admin-server/conf/eventmesh-admin.properties
diff --git a/eventmesh-admin-server/src/main/resources/eventmesh.sql b/eventmesh-admin-server/conf/eventmesh.sql
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/eventmesh.sql
rename to eventmesh-admin-server/conf/eventmesh.sql
diff --git a/eventmesh-admin-server/conf/log4j2.xml b/eventmesh-admin-server/conf/log4j2.xml
new file mode 100644
index 0000000000..6341a0e629
--- /dev/null
+++ b/eventmesh-admin-server/conf/log4j2.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshDataSourceMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshDataSourceMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshDataSourceMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshDataSourceMapper.xml
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshJobInfoMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshJobInfoMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshJobInfoMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshJobInfoMapper.xml
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshMysqlPositionMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshMysqlPositionMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshMysqlPositionMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshMysqlPositionMapper.xml
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshPositionReporterHistoryMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshPositionReporterHistoryMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshPositionReporterHistoryMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshPositionReporterHistoryMapper.xml
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshRuntimeHeartbeatMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHeartbeatMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshRuntimeHeartbeatMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshRuntimeHeartbeatMapper.xml
diff --git a/eventmesh-admin-server/src/main/resources/mapper/EventMeshRuntimeHistoryMapper.xml b/eventmesh-admin-server/conf/mapper/EventMeshRuntimeHistoryMapper.xml
similarity index 100%
rename from eventmesh-admin-server/src/main/resources/mapper/EventMeshRuntimeHistoryMapper.xml
rename to eventmesh-admin-server/conf/mapper/EventMeshRuntimeHistoryMapper.xml
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/Admin.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/Admin.java
index 71c6d67be2..9ee25fadb2 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/Admin.java
+++ b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/Admin.java
@@ -17,6 +17,7 @@
package org.apache.eventmesh.admin.server;
+import org.apache.eventmesh.common.ComponentLifeCycle;
import org.apache.eventmesh.common.remote.Task;
import org.apache.eventmesh.common.remote.request.ReportHeartBeatRequest;
import org.apache.eventmesh.common.utils.PagedList;
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServer.java
index 98247d19b6..a2e4cc7063 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServer.java
+++ b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/AdminServer.java
@@ -102,7 +102,7 @@ public void start() {
}
@Override
- public void destroy() {
+ public void stop() {
if (configuration.isEventMeshRegistryPluginEnabled()) {
registryService.unRegister(adminServeInfo);
try {
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java
index c6a6e16504..7f5fa22dda 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java
+++ b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ExampleAdminServer.java
@@ -23,7 +23,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-@SpringBootApplication
+@SpringBootApplication()
public class ExampleAdminServer {
public static void main(String[] args) throws Exception {
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java
index 24085dd89e..9bbe4ce305 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java
+++ b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/BaseServer.java
@@ -17,7 +17,7 @@
package org.apache.eventmesh.admin.server.web;
-import org.apache.eventmesh.admin.server.ComponentLifeCycle;
+import org.apache.eventmesh.common.ComponentLifeCycle;
import org.apache.eventmesh.common.remote.payload.PayloadFactory;
import javax.annotation.PostConstruct;
@@ -40,9 +40,9 @@ public void init() throws Exception {
}
@PreDestroy
- public void shutdown() {
+ public void shutdown() throws Exception {
log.info("[{}] server will destroy", this.getClass().getSimpleName());
- destroy();
+ stop();
log.info("[{}] server has be destroy", this.getClass().getSimpleName());
}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java
index 5fbb34f489..572e07a21d 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java
+++ b/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/web/GrpcServer.java
@@ -52,7 +52,7 @@ public void start() throws Exception {
}
@Override
- public void destroy() {
+ public void stop() {
try {
if (server != null) {
server.shutdown();
diff --git a/eventmesh-common/build.gradle b/eventmesh-common/build.gradle
index 70244d2299..c95e9f6c29 100644
--- a/eventmesh-common/build.gradle
+++ b/eventmesh-common/build.gradle
@@ -48,6 +48,7 @@ dependencies {
implementation "org.apache.httpcomponents:httpclient"
implementation "io.netty:netty-all"
+ compileOnly 'com.mysql:mysql-connector-j'
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/AbstractComponent.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/AbstractComponent.java
new file mode 100644
index 0000000000..375b6cb1d3
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/AbstractComponent.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public abstract class AbstractComponent implements ComponentLifeCycle {
+ private final AtomicBoolean started = new AtomicBoolean(false);
+ private final AtomicBoolean stopped = new AtomicBoolean(false);
+
+ @Override
+ public void start() throws Exception {
+ if (!started.compareAndSet(false, true)) {
+ log.info("component [{}] has started", this.getClass());
+ return;
+ }
+ log.info("component [{}] will start", this.getClass());
+ run();
+ log.info("component [{}] started successfully", this.getClass());
+ }
+
+ @Override
+ public void stop() throws Exception {
+ if (!stopped.compareAndSet(false, true)) {
+ log.info("component [{}] has stopped", this.getClass());
+ return;
+ }
+ log.info("component [{}] will stop", this.getClass());
+ shutdown();
+ log.info("component [{}] stopped successfully", this.getClass());
+ }
+
+ protected abstract void run() throws Exception;
+
+ protected abstract void shutdown() throws Exception;
+}
diff --git a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ComponentLifeCycle.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/ComponentLifeCycle.java
similarity index 89%
rename from eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ComponentLifeCycle.java
rename to eventmesh-common/src/main/java/org/apache/eventmesh/common/ComponentLifeCycle.java
index 392eebfbba..76fdd548d0 100644
--- a/eventmesh-admin-server/src/main/java/org/apache/eventmesh/admin/server/ComponentLifeCycle.java
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/ComponentLifeCycle.java
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-package org.apache.eventmesh.admin.server;
+package org.apache.eventmesh.common;
/**
- * adminServer ComponentLifeCycle
+ * LifeCycle of EventMesh Component
*/
public interface ComponentLifeCycle {
void start() throws Exception;
- void destroy();
+ void stop() throws Exception;
}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/JdbcConfig.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/JdbcConfig.java
new file mode 100644
index 0000000000..fc784fc187
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/JdbcConfig.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb;
+
+import org.apache.eventmesh.common.config.connector.rdb.canal.RdbDBDefinition;
+
+import java.util.Set;
+
+import lombok.Data;
+
+@Data
+public class JdbcConfig {
+ private String url;
+
+ private String dbAddress;
+
+ private int dbPort;
+
+ private String userName;
+
+ private String passWord;
+
+ private Set databases;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalMySQLType.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalMySQLType.java
new file mode 100644
index 0000000000..b5107ccbf3
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalMySQLType.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.eventmesh.common.config.connector.rdb.canal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.mysql.cj.MysqlType;
+
+public enum CanalMySQLType {
+ BIT("BIT"),
+ TINYINT("TINYINT"),
+ SMALLINT("SMALLINT"),
+ MEDIUMINT("MEDIUMINT"),
+ INT("INT"),
+ BIGINT("BIGINT"),
+ DECIMAL("DECIMAL"),
+ FLOAT("FLOAT"),
+ DOUBLE("DOUBLE"),
+ DATE("DATE"),
+ DATETIME("DATETIME"),
+ TIMESTAMP("TIMESTAMP"),
+ TIME("TIME"),
+ YEAR("YEAR"),
+ CHAR("CHAR"),
+ VARCHAR("VARCHAR"),
+ BINARY("BINARY"),
+ VARBINARY("VARBINARY"),
+ TINYBLOB("TINYBLOB"),
+ BLOB("BLOB"),
+ MEDIUMBLOB("MEDIUMBLOB"),
+ LONGBLOB("LONGBLOB"),
+ TINYTEXT("TINYTEXT"),
+ TEXT("TEXT"),
+ MEDIUMTEXT("MEDIUMTEXT"),
+ LONGTEXT("LONGTEXT"),
+ ENUM("ENUM"),
+ SET("SET"),
+ JSON("JSON"),
+ GEOMETRY("GEOMETRY"),
+ // MysqlType not include the following type
+ POINT("POINT"),
+ LINESTRING("LINESTRING"),
+ POLYGON("POLYGON"),
+ MULTIPOINT("MULTIPOINT"),
+ GEOMETRY_COLLECTION("GEOMETRYCOLLECTION"),
+ GEOM_COLLECTION("GEOMCOLLECTION"),
+ MULTILINESTRING("MULTILINESTRING"),
+ MULTIPOLYGON("MULTIPOLYGON");
+
+ private final String codeKey;
+ private final MysqlType mysqlType;
+
+ CanalMySQLType(String codeKey) {
+ this.codeKey = codeKey;
+ this.mysqlType = MysqlType.getByName(codeKey);
+ }
+
+ private static final Map TYPES = new HashMap<>();
+
+ static {
+ CanalMySQLType[] values = values();
+ for (CanalMySQLType tableType : values) {
+ TYPES.put(tableType.codeKey, tableType);
+ }
+ }
+
+ public String genPrepareStatement4Insert() {
+ switch (this) {
+ case GEOMETRY:
+ case GEOM_COLLECTION:
+ case GEOMETRY_COLLECTION:
+ return "ST_GEOMFROMTEXT(?)";
+ case POINT:
+ return "ST_PointFromText(?)";
+ case LINESTRING:
+ return "ST_LineStringFromText(?)";
+ case POLYGON:
+ return "ST_PolygonFromText(?)";
+ case MULTIPOINT:
+ return "ST_MultiPointFromText(?)";
+ case MULTILINESTRING:
+ return "ST_MultiLineStringFromText(?)";
+ case MULTIPOLYGON:
+ return "ST_MultiPolygonFromText(?)";
+ default:
+ return "?";
+ }
+ }
+
+ public static CanalMySQLType valueOfCode(String code) {
+ CanalMySQLType type = TYPES.get(code.toUpperCase());
+ if (type != null) {
+ return type;
+ }
+ switch (MysqlType.getByName(code)) {
+ case BOOLEAN:
+ case TINYINT:
+ case TINYINT_UNSIGNED:
+ return TINYINT;
+ case SMALLINT:
+ case SMALLINT_UNSIGNED:
+ return SMALLINT;
+ case INT:
+ case INT_UNSIGNED:
+ return INT;
+ case BIGINT:
+ case BIGINT_UNSIGNED:
+ return BIGINT;
+ case MEDIUMINT:
+ case MEDIUMINT_UNSIGNED:
+ return MEDIUMINT;
+ case DECIMAL:
+ case DECIMAL_UNSIGNED:
+ return DECIMAL;
+ case FLOAT:
+ case FLOAT_UNSIGNED:
+ return FLOAT;
+ case DOUBLE:
+ case DOUBLE_UNSIGNED:
+ return DOUBLE;
+ case BIT:
+ return BIT;
+ case BINARY:
+ return BINARY;
+ case VARBINARY:
+ return VARBINARY;
+ case TINYBLOB:
+ return TINYBLOB;
+ case MEDIUMBLOB:
+ return MEDIUMBLOB;
+ case LONGBLOB:
+ return LONGBLOB;
+ case BLOB:
+ return BLOB;
+ case CHAR:
+ return CHAR;
+ case VARCHAR:
+ return VARCHAR;
+ case TINYTEXT:
+ return TINYTEXT;
+ case MEDIUMTEXT:
+ return MEDIUMTEXT;
+ case LONGTEXT:
+ return LONGTEXT;
+ case TEXT:
+ return TEXT;
+ case DATE:
+ return DATE;
+ case TIME:
+ return TIME;
+ case TIMESTAMP:
+ return TIMESTAMP;
+ case DATETIME:
+ return DATETIME;
+ case YEAR:
+ return YEAR;
+ case JSON:
+ return JSON;
+ case ENUM:
+ return ENUM;
+ case SET:
+ return SET;
+ case GEOMETRY:
+ return GEOMETRY;
+ case NULL:
+ case UNKNOWN:
+ default:
+ throw new UnsupportedOperationException("Unsupported mysql columnType " + code);
+ }
+ }
+
+ public MysqlType getMysqlType() {
+ return mysqlType;
+ }
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSinkFullConfig.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSinkFullConfig.java
new file mode 100644
index 0000000000..c2b881df6c
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSinkFullConfig.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb.canal;
+
+import org.apache.eventmesh.common.config.connector.SinkConfig;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CanalSinkFullConfig extends SinkConfig {
+ private SinkConnectorConfig sinkConfig;
+ private String zeroDate;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSourceFullConfig.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSourceFullConfig.java
new file mode 100644
index 0000000000..a2ab8ba31d
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/CanalSourceFullConfig.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb.canal;
+
+import org.apache.eventmesh.common.config.connector.SourceConfig;
+import org.apache.eventmesh.common.remote.offset.RecordPosition;
+
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CanalSourceFullConfig extends SourceConfig {
+ private SourceConnectorConfig connectorConfig;
+ private List startPosition;
+ private int parallel;
+ private int flushSize;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/JobRdbFullPosition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/JobRdbFullPosition.java
new file mode 100644
index 0000000000..08f88e1d24
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/JobRdbFullPosition.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb.canal;
+
+import java.math.BigDecimal;
+
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class JobRdbFullPosition {
+ private String jobId;
+ private String schema;
+ private String tableName;
+ private String primaryKeyRecords;
+ private long maxCount;
+ private boolean finished;
+ private BigDecimal percent;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbColumnDefinition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbColumnDefinition.java
new file mode 100644
index 0000000000..94c0135c3e
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbColumnDefinition.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.eventmesh.common.config.connector.rdb.canal;
+
+import java.sql.JDBCType;
+
+import lombok.Data;
+
+@Data
+public class RdbColumnDefinition {
+ protected String name;
+ protected JDBCType jdbcType;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbDBDefinition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbDBDefinition.java
new file mode 100644
index 0000000000..ab3ed336f8
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbDBDefinition.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb.canal;
+
+import java.util.Set;
+
+import lombok.Data;
+
+/**
+ * Description: as class name
+ */
+@Data
+public class RdbDBDefinition {
+ private String schemaName;
+ private Set tables;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbTableDefinition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbTableDefinition.java
new file mode 100644
index 0000000000..c281035578
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/RdbTableDefinition.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.eventmesh.common.config.connector.rdb.canal;
+
+import lombok.Data;
+
+/**
+ * Description: as class name
+ */
+@Data
+public class RdbTableDefinition {
+ protected String schemaName;
+ protected String tableName;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SinkConnectorConfig.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SinkConnectorConfig.java
index 1124bb1425..761cdba4bb 100644
--- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SinkConnectorConfig.java
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SinkConnectorConfig.java
@@ -17,27 +17,16 @@
package org.apache.eventmesh.common.config.connector.rdb.canal;
+import org.apache.eventmesh.common.config.connector.rdb.JdbcConfig;
+
import lombok.Data;
+import lombok.EqualsAndHashCode;
/**
* Configuration parameters for a sink connector.
*/
@Data
-public class SinkConnectorConfig {
-
+@EqualsAndHashCode(callSuper = true)
+public class SinkConnectorConfig extends JdbcConfig {
private String connectorName;
-
- private String url;
-
- private String dbAddress;
-
- private int dbPort;
-
- private String userName;
-
- private String passWord;
-
- private String schemaName;
-
- private String tableName;
}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SourceConnectorConfig.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SourceConnectorConfig.java
index e9ae466079..9a95696a0d 100644
--- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SourceConnectorConfig.java
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/SourceConnectorConfig.java
@@ -17,28 +17,16 @@
package org.apache.eventmesh.common.config.connector.rdb.canal;
+import org.apache.eventmesh.common.config.connector.rdb.JdbcConfig;
+
import lombok.Data;
+import lombok.EqualsAndHashCode;
/**
* Represents the configuration for a database connector.
*/
@Data
-public class SourceConnectorConfig {
-
+@EqualsAndHashCode(callSuper = true)
+public class SourceConnectorConfig extends JdbcConfig {
private String connectorName;
-
- private String url;
-
- private String dbAddress;
-
- private int dbPort;
-
- private String userName;
-
- private String passWord;
-
- private String schemaName;
-
- private String tableName;
-
}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/Constants.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/Constants.java
new file mode 100644
index 0000000000..8c51c7255b
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/Constants.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.common.config.connector.rdb.canal.mysql;
+
+public class Constants {
+ public static final String MySQLQuot = "`";
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLColumnDef.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLColumnDef.java
new file mode 100644
index 0000000000..cdc9adf33f
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLColumnDef.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.eventmesh.common.config.connector.rdb.canal.mysql;
+
+import org.apache.eventmesh.common.config.connector.rdb.canal.CanalMySQLType;
+import org.apache.eventmesh.common.config.connector.rdb.canal.RdbColumnDefinition;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MySQLColumnDef extends RdbColumnDefinition {
+ private CanalMySQLType type;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLTableDef.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLTableDef.java
new file mode 100644
index 0000000000..cdd3652378
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/config/connector/rdb/canal/mysql/MySQLTableDef.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.eventmesh.common.config.connector.rdb.canal.mysql;
+
+import org.apache.eventmesh.common.config.connector.rdb.canal.RdbTableDefinition;
+
+import java.util.Map;
+import java.util.Set;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Description:
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MySQLTableDef extends RdbTableDefinition {
+ private Set primaryKeys;
+ private Map columnDefinitions;
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/RecordPosition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/RecordPosition.java
index c3c2d5dd7a..5f45390b73 100644
--- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/RecordPosition.java
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/RecordPosition.java
@@ -19,6 +19,8 @@
import org.apache.eventmesh.common.remote.offset.S3.S3RecordOffset;
import org.apache.eventmesh.common.remote.offset.S3.S3RecordPartition;
+import org.apache.eventmesh.common.remote.offset.canal.CanalFullRecordOffset;
+import org.apache.eventmesh.common.remote.offset.canal.CanalFullRecordPartition;
import org.apache.eventmesh.common.remote.offset.canal.CanalRecordOffset;
import org.apache.eventmesh.common.remote.offset.canal.CanalRecordPartition;
import org.apache.eventmesh.common.remote.offset.file.FileRecordOffset;
@@ -40,6 +42,7 @@ public class RecordPosition {
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonSubTypes({
@JsonSubTypes.Type(value = CanalRecordPartition.class, name = "CanalRecordPartition"),
+ @JsonSubTypes.Type(value = CanalFullRecordPartition.class, name = "CanalFullRecordPartition"),
@JsonSubTypes.Type(value = FileRecordPartition.class, name = "FileRecordPartition"),
@JsonSubTypes.Type(value = S3RecordPartition.class, name = "S3RecordPartition"),
@JsonSubTypes.Type(value = KafkaRecordPartition.class, name = "KafkaRecordPartition"),
@@ -53,6 +56,7 @@ public class RecordPosition {
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonSubTypes({
@JsonSubTypes.Type(value = CanalRecordOffset.class, name = "CanalRecordOffset"),
+ @JsonSubTypes.Type(value = CanalFullRecordOffset.class, name = "CanalFullRecordOffset"),
@JsonSubTypes.Type(value = FileRecordOffset.class, name = "FileRecordOffset"),
@JsonSubTypes.Type(value = S3RecordOffset.class, name = "S3RecordOffset"),
@JsonSubTypes.Type(value = KafkaRecordOffset.class, name = "KafkaRecordOffset"),
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordOffset.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordOffset.java
new file mode 100644
index 0000000000..a0a077b7f5
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordOffset.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.eventmesh.common.remote.offset.canal;
+
+import org.apache.eventmesh.common.config.connector.rdb.canal.JobRdbFullPosition;
+import org.apache.eventmesh.common.remote.offset.RecordOffset;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString
+public class CanalFullRecordOffset extends RecordOffset {
+ private JobRdbFullPosition position;
+
+ @Override
+ public Class extends RecordOffset> getRecordOffsetClass() {
+ return CanalFullRecordOffset.class;
+ }
+}
diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordPartition.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordPartition.java
new file mode 100644
index 0000000000..73626fa78f
--- /dev/null
+++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/remote/offset/canal/CanalFullRecordPartition.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.eventmesh.common.remote.offset.canal;
+
+import org.apache.eventmesh.common.remote.offset.RecordPartition;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+
+@Data
+@ToString
+@EqualsAndHashCode(callSuper = true)
+public class CanalFullRecordPartition extends RecordPartition {
+
+ @Override
+ public Class extends RecordPartition> getRecordPartitionClass() {
+ return CanalFullRecordPartition.class;
+ }
+}
diff --git a/eventmesh-connectors/eventmesh-connector-canal/build.gradle b/eventmesh-connectors/eventmesh-connector-canal/build.gradle
index ccc5acf0ca..134af8ed3e 100644
--- a/eventmesh-connectors/eventmesh-connector-canal/build.gradle
+++ b/eventmesh-connectors/eventmesh-connector-canal/build.gradle
@@ -23,9 +23,11 @@ List canal = [
dependencies {
api project(":eventmesh-openconnect:eventmesh-openconnect-java")
+ implementation "org.locationtech.jts:jts-core"
implementation project(":eventmesh-common")
implementation canal
- implementation "com.alibaba:druid:1.2.23"
+ implementation "com.alibaba:druid"
+ implementation 'com.mysql:mysql-connector-j'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation "org.mockito:mockito-core"
diff --git a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/DatabaseConnection.java b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/DatabaseConnection.java
index 0d9da7f8be..0310e5434c 100644
--- a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/DatabaseConnection.java
+++ b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/DatabaseConnection.java
@@ -18,8 +18,8 @@
package org.apache.eventmesh.connector.canal;
-import org.apache.eventmesh.common.config.connector.rdb.canal.CanalSinkConfig;
-import org.apache.eventmesh.common.config.connector.rdb.canal.CanalSourceConfig;
+import org.apache.eventmesh.common.config.connector.rdb.canal.SinkConnectorConfig;
+import org.apache.eventmesh.common.config.connector.rdb.canal.SourceConnectorConfig;
import java.sql.Connection;
import java.sql.SQLException;
@@ -32,46 +32,40 @@ public class DatabaseConnection {
public static DruidDataSource sinkDataSource;
- public static CanalSourceConfig sourceConfig;
+ public static SourceConnectorConfig sourceConfig;
- public static CanalSinkConfig sinkConfig;
+ public static SinkConnectorConfig sinkConfig;
+
+ public static DruidDataSource createDruidDataSource(String url, String userName, String passWord) {
+ DruidDataSource dataSource = new DruidDataSource();
+ dataSource.setUrl(url);
+ dataSource.setUsername(userName);
+ dataSource.setPassword(passWord);
+ dataSource.setInitialSize(5);
+ dataSource.setMinIdle(5);
+ dataSource.setMaxActive(20);
+ dataSource.setMaxWait(60000);
+ dataSource.setTimeBetweenEvictionRunsMillis(60000);
+ dataSource.setMinEvictableIdleTimeMillis(300000);
+ dataSource.setValidationQuery("SELECT 1");
+ dataSource.setTestWhileIdle(true);
+ dataSource.setTestOnBorrow(false);
+ dataSource.setTestOnReturn(false);
+ dataSource.setPoolPreparedStatements(true);
+ dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
+ return dataSource;
+ }
public static void initSourceConnection() {
- sourceDataSource = new DruidDataSource();
- sourceDataSource.setUrl(sourceConfig.getSourceConnectorConfig().getUrl());
- sourceDataSource.setUsername(sourceConfig.getSourceConnectorConfig().getUserName());
- sourceDataSource.setPassword(sourceConfig.getSourceConnectorConfig().getPassWord());
- sourceDataSource.setInitialSize(5);
- sourceDataSource.setMinIdle(5);
- sourceDataSource.setMaxActive(20);
- sourceDataSource.setMaxWait(60000);
- sourceDataSource.setTimeBetweenEvictionRunsMillis(60000);
- sourceDataSource.setMinEvictableIdleTimeMillis(300000);
- sourceDataSource.setValidationQuery("SELECT 1");
- sourceDataSource.setTestWhileIdle(true);
- sourceDataSource.setTestOnBorrow(false);
- sourceDataSource.setTestOnReturn(false);
- sourceDataSource.setPoolPreparedStatements(true);
- sourceDataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
+ sourceDataSource = createDruidDataSource(sourceConfig.getUrl(),
+ sourceConfig.getUserName(),
+ sourceConfig.getPassWord());
}
public static void initSinkConnection() {
- sinkDataSource = new DruidDataSource();
- sinkDataSource.setUrl(sinkConfig.getSinkConnectorConfig().getUrl());
- sinkDataSource.setUsername(sinkConfig.getSinkConnectorConfig().getUserName());
- sinkDataSource.setPassword(sinkConfig.getSinkConnectorConfig().getPassWord());
- sinkDataSource.setInitialSize(5);
- sinkDataSource.setMinIdle(5);
- sinkDataSource.setMaxActive(20);
- sinkDataSource.setMaxWait(60000);
- sinkDataSource.setTimeBetweenEvictionRunsMillis(60000);
- sinkDataSource.setMinEvictableIdleTimeMillis(300000);
- sinkDataSource.setValidationQuery("SELECT 1");
- sinkDataSource.setTestWhileIdle(true);
- sinkDataSource.setTestOnBorrow(false);
- sinkDataSource.setTestOnReturn(false);
- sinkDataSource.setPoolPreparedStatements(true);
- sinkDataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
+ sinkDataSource = createDruidDataSource(sinkConfig.getUrl(),
+ sinkConfig.getUserName(),
+ sinkConfig.getPassWord());
}
diff --git a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/SqlUtils.java b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/SqlUtils.java
index f6c4329e23..1008ad1cf3 100644
--- a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/SqlUtils.java
+++ b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/SqlUtils.java
@@ -30,25 +30,45 @@
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
+import java.sql.JDBCType;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.temporal.Temporal;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.io.WKBReader;
+import org.locationtech.jts.io.WKTReader;
+
+import com.mysql.cj.Constants;
+import com.mysql.cj.MysqlType;
+import com.taobao.tddl.dbsync.binlog.LogBuffer;
public class SqlUtils {
public static final String REQUIRED_FIELD_NULL_SUBSTITUTE = " ";
- public static final String SQLDATE_FORMAT = "yyyy-MM-dd";
- public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final Map> sqlTypeToJavaTypeMap = new HashMap>();
private static final ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();
- private static final Logger log = LoggerFactory.getLogger(SqlUtils.class);
+ private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
+ private static final WKBReader WKB_READER = new WKBReader(GEOMETRY_FACTORY);
+ private static final BigDecimal NANO_SEC = new BigDecimal(LogBuffer.DIG_BASE);
+ private static final LocalDateTime BASE = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0);
+ private static final long ONE_HOUR = 3600;
+ private static final long ONE_MINUTE = 60;
static {
// regist Converter
@@ -109,6 +129,29 @@ public class SqlUtils {
sqlTypeToJavaTypeMap.put(Types.CLOB, String.class);
}
+ public static String genPrepareSqlOfInClause(int size) {
+ StringBuilder sql = new StringBuilder();
+ sql.append("(");
+ for (int i = 0; i < size; i++) {
+ sql.append("?");
+ if (i < size - 1) {
+ sql.append(",");
+ }
+ }
+ sql.append(")");
+ return sql.toString();
+ }
+
+ public static void setInClauseParameters(PreparedStatement preparedStatement, List params) throws SQLException {
+ setInClauseParameters(preparedStatement, 0, params);
+ }
+
+ public static void setInClauseParameters(PreparedStatement preparedStatement, int paramIndexStart, List params) throws SQLException {
+ for (int i = 0; i < params.size(); i++) {
+ preparedStatement.setString(paramIndexStart + i, params.get(i));
+ }
+ }
+
public static String sqlValueToString(ResultSet rs, int index, int sqlType) throws SQLException {
Class> requiredType = sqlTypeToJavaTypeMap.get(sqlType);
if (requiredType == null) {
@@ -217,8 +260,7 @@ private static String getResultSetValue(ResultSet rs, int index, Class> requir
} else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {
value = rs.getFloat(index);
wasNullCheck = true;
- } else if (double.class.equals(requiredType) || Double.class.equals(requiredType)
- || Number.class.equals(requiredType)) {
+ } else if (double.class.equals(requiredType) || Double.class.equals(requiredType) || Number.class.equals(requiredType)) {
value = rs.getDouble(index);
wasNullCheck = true;
} else if (Time.class.equals(requiredType)) {
@@ -282,15 +324,598 @@ private static String getResultSetValue(ResultSet rs, int index) throws SQLExcep
* Check whether the given SQL type is numeric.
*/
public static boolean isNumeric(int sqlType) {
- return (Types.BIT == sqlType) || (Types.BIGINT == sqlType) || (Types.DECIMAL == sqlType)
- || (Types.DOUBLE == sqlType) || (Types.FLOAT == sqlType) || (Types.INTEGER == sqlType)
- || (Types.NUMERIC == sqlType) || (Types.REAL == sqlType) || (Types.SMALLINT == sqlType)
- || (Types.TINYINT == sqlType);
+ return (Types.BIT == sqlType) || (Types.BIGINT == sqlType) || (Types.DECIMAL == sqlType) || (Types.DOUBLE == sqlType)
+ || (Types.FLOAT == sqlType) || (Types.INTEGER == sqlType) || (Types.NUMERIC == sqlType) || (Types.REAL == sqlType)
+ || (Types.SMALLINT == sqlType) || (Types.TINYINT == sqlType);
}
public static boolean isTextType(int sqlType) {
- return sqlType == Types.CHAR || sqlType == Types.VARCHAR || sqlType == Types.CLOB || sqlType == Types.LONGVARCHAR
- || sqlType == Types.NCHAR || sqlType == Types.NVARCHAR || sqlType == Types.NCLOB
- || sqlType == Types.LONGNVARCHAR;
+ return sqlType == Types.CHAR || sqlType == Types.VARCHAR || sqlType == Types.CLOB || sqlType == Types.LONGVARCHAR || sqlType == Types.NCHAR
+ || sqlType == Types.NVARCHAR || sqlType == Types.NCLOB || sqlType == Types.LONGNVARCHAR;
+ }
+
+ public static JDBCType toJDBCType(String connectorDataType) {
+ MysqlType mysqlType = MysqlType.getByName(connectorDataType);
+ return JDBCType.valueOf(mysqlType.getJdbcType());
+ }
+
+ public static BigDecimal toBigDecimal(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strValue = (String) value;
+ if (!org.apache.commons.lang3.StringUtils.isNotBlank(strValue)) {
+ return null;
+ }
+ try {
+ return new BigDecimal(strValue);
+ } catch (Exception e) {
+ if ("true".equals(strValue)) {
+ return BigDecimal.ONE;
+ }
+ if ("false".equals(strValue)) {
+ return BigDecimal.ZERO;
+ }
+ return new BigDecimal(strValue);
+ }
+ } else if (value instanceof Number) {
+ if (value instanceof BigDecimal) {
+ return (BigDecimal) value;
+ }
+ if (value instanceof Integer) {
+ return BigDecimal.valueOf(((Integer) value).longValue());
+ }
+ if (value instanceof Long) {
+ return BigDecimal.valueOf(((Long) value));
+ }
+ if (value instanceof Double) {
+ return BigDecimal.valueOf(((Double) value));
+ }
+ if (value instanceof Float) {
+ return BigDecimal.valueOf(((Float) value).doubleValue());
+ }
+ if (value instanceof BigInteger) {
+ return new BigDecimal((BigInteger) value);
+ }
+ if (value instanceof Byte) {
+ return BigDecimal.valueOf(((Byte) value).longValue());
+ }
+ if (value instanceof Short) {
+ return BigDecimal.valueOf(((Short) value).longValue());
+ }
+ return null;
+ } else if (value instanceof Boolean) {
+ return Boolean.TRUE.equals(value) ? BigDecimal.ONE : BigDecimal.ZERO;
+ } else {
+ throw new UnsupportedOperationException("class " + value.getClass() + ", value '" + value + "' , parse to big decimal failed.");
+ }
+ }
+
+ public static Double toDouble(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strValue = (String) value;
+ if (org.apache.commons.lang3.StringUtils.isBlank(strValue)) {
+ return null;
+ }
+ try {
+ return Double.parseDouble(strValue);
+ } catch (Exception e) {
+ if ("true".equals(strValue)) {
+ return 1.0d;
+ }
+ if ("false".equals(strValue)) {
+ return 0.0d;
+ }
+ return new BigDecimal(strValue).doubleValue();
+ }
+ } else if (value instanceof Number) {
+ return ((Number) value).doubleValue();
+ } else {
+ if (value instanceof Boolean) {
+ return Boolean.TRUE.equals(value) ? 1.0d : 0.0d;
+ }
+ throw new UnsupportedOperationException("class " + value.getClass() + ", value '" + value + "' , parse to double failed.");
+ }
+ }
+
+ public static Long toLong(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strValue = (String) value;
+ if (org.apache.commons.lang3.StringUtils.isBlank(strValue)) {
+ return null;
+ }
+ try {
+ return Long.parseLong(strValue);
+ } catch (Exception e) {
+ try {
+ return Long.decode(strValue);
+ } catch (Exception e2) {
+ if ("true".equals(strValue)) {
+ return 1L;
+ }
+ if ("false".equals(strValue)) {
+ return 0L;
+ }
+ return new BigDecimal(strValue).longValue();
+ }
+ }
+ } else if (value instanceof Number) {
+ return ((Number) value).longValue();
+ } else {
+ if (value instanceof Boolean) {
+ return Boolean.TRUE.equals(value) ? 1L : 0L;
+ }
+ throw new UnsupportedOperationException(value.getClass() + ", value '" + value + "' , parse to long failed.");
+ }
+ }
+
+ public static boolean isZeroTime(Object value) {
+ if (value == null || org.apache.commons.lang3.StringUtils.isBlank(value.toString())) {
+ return false;
+ }
+ return value.toString().startsWith("0000-00-00");
+ }
+
+ public static String removeZone(String datetime) {
+ if (datetime == null || datetime.length() == 0) {
+ return datetime;
+ }
+ int len = datetime.length();
+ if (datetime.charAt(len - 1) == 'Z' || datetime.charAt(len - 1) == 'z') {
+ return datetime.substring(0, len - 1).trim();
+ }
+ if (len >= 7) {
+ char checkCharAt1 = datetime.charAt(len - 2);
+ if ((checkCharAt1 == '+' || checkCharAt1 == '-') && len >= 10) {
+ return datetime.substring(0, len - 2).trim();
+ }
+ char checkCharAt2 = datetime.charAt(len - 3);
+ if ((checkCharAt2 == '+' || checkCharAt2 == '-') && len >= 11) {
+ return datetime.substring(0, len - 3).trim();
+ }
+ char checkCharAt3 = datetime.charAt(len - 6);
+ if ((checkCharAt3 == '+' || checkCharAt3 == '-') && checkCharAt2 == ':') {
+ return datetime.substring(0, len - 6).trim();
+ }
+ char checkCharAt4 = datetime.charAt(len - 5);
+ if ((checkCharAt4 == '+' || checkCharAt4 == '-') && checkCharAt2 == ':') {
+ return datetime.substring(0, len - 5).trim();
+ }
+ char checkCharAt5 = len >= 9 ? datetime.charAt(len - 9) : ' ';
+ if ((checkCharAt5 == '+' || checkCharAt5 == '-') && checkCharAt2 == ':' && checkCharAt3 == ':') {
+ return datetime.substring(0, len - 9).trim();
+ }
+ char checkCharAt6 = datetime.charAt(len - 7);
+ if (checkCharAt6 == '+' || checkCharAt6 == '-') {
+ return datetime.substring(0, len - 7).trim();
+ }
+ if (checkCharAt4 == '+' || checkCharAt4 == '-') {
+ return datetime.substring(0, len - 5).trim();
+ }
+ }
+ return datetime;
+ }
+
+
+
+ public static String bytes2hex(byte[] b) {
+ if (b == null) {
+ return null;
+ }
+ if (b.length == 0) {
+ return "";
+ }
+ StringBuilder hs = new StringBuilder();
+ for (byte element : b) {
+ String stmp = Integer.toHexString(element & 255).toUpperCase();
+ if (stmp.length() == 1) {
+ hs.append(Constants.CJ_MINOR_VERSION);
+ hs.append(stmp);
+ } else {
+ hs.append(stmp);
+ }
+ }
+ return hs.toString();
+ }
+
+ public static String convertToString(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return (String) value;
+ }
+ if (value instanceof BigInteger) {
+ return value.toString();
+ }
+ if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).toPlainString();
+ }
+ if (value instanceof Number) {
+ return new BigDecimal(value.toString()).toPlainString();
+ }
+ if (value instanceof Boolean) {
+ return Boolean.TRUE.equals(value) ? "1" : "0";
+ }
+ if (value instanceof byte[]) {
+ return "0x" + bytes2hex((byte[]) value);
+ }
+ if (value instanceof Timestamp) {
+ long nanos = ((Timestamp) value).getNanos();
+ value = Instant.ofEpochMilli(((Timestamp) value).getTime() - (nanos / 1000000)).plusNanos(nanos).atZone(ZoneId.systemDefault())
+ .toLocalDateTime();
+ } else if (value instanceof Date) {
+ value = ((Date) value).toLocalDate().atTime(0, 0);
+ } else if (value instanceof Time) {
+ value = LocalDateTime.of(LocalDate.of(1970, 1, 1),
+ Instant.ofEpochMilli(((Time) value).getTime()).atZone(ZoneId.systemDefault()).toLocalTime());
+ } else if (value instanceof java.util.Date) {
+ value = ((java.util.Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
+ if (value instanceof LocalDateTime) {
+ return coverLocalDateTime2String((LocalDateTime) value);
+ } else if (value instanceof OffsetDateTime) {
+ OffsetDateTime zone = (OffsetDateTime) value;
+ String datetimeStr = coverLocalDateTime2String(zone.toLocalDateTime());
+ String zonedStr = zone.getOffset().toString();
+ if ("Z".equals(zonedStr)) {
+ return datetimeStr + "+00:00";
+ }
+ return datetimeStr + zonedStr;
+ } else if (!(value instanceof LocalTime)) {
+ return value.toString();
+ } else {
+ LocalTime local3 = (LocalTime) value;
+ return String.format("%02d:%02d:%02d", local3.getHour(), local3.getMinute(), local3.getSecond());
+ }
+ }
+
+
+ private static String coverLocalDateTime2String(LocalDateTime localDateTime) {
+ LocalDate localDate = localDateTime.toLocalDate();
+ LocalTime localTime = localDateTime.toLocalTime();
+ int year = localDate.getYear();
+ int month = localDate.getMonthValue();
+ int day = localDate.getDayOfMonth();
+ int hour = localTime.getHour();
+ int minute = localTime.getMinute();
+ int second = localTime.getSecond();
+ int nano = localTime.getNano();
+ return nano == 0 ? String.format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second) :
+ String.format("%04d-%02d-%02d %02d:%02d:%02d.%s", year, month, day, hour, minute, second,
+ new BigDecimal(nano).divide(NANO_SEC).toPlainString().substring(2));
+ }
+
+ public static String toMySqlTime(Object value) {
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ return null;
+ }
+ if (value instanceof String) {
+ return value.toString();
+ }
+ LocalDateTime localTime = toLocalDateTime(value);
+ if (BASE.isBefore(localTime) || BASE.isEqual(localTime)) {
+ long diffHours = Duration.between(BASE, localTime).toHours();
+ if (localTime.getNano() == 0) {
+ return String.format("%02d:%02d:%02d", diffHours, localTime.getMinute(), localTime.getSecond());
+ }
+ return String.format("%02d:%02d:%02d.%s", diffHours, localTime.getMinute(), localTime.getSecond(),
+ Integer.parseInt(trimEnd(String.valueOf(localTime.getNano()), '0')));
+ }
+ Duration duration = Duration.between(localTime, BASE);
+ long totalSecond = duration.getSeconds();
+ long hours = totalSecond / ONE_HOUR;
+ long remaining = totalSecond - (hours * ONE_HOUR);
+ long minutes = remaining / ONE_MINUTE;
+ remaining = remaining - (minutes * ONE_MINUTE);
+ if (duration.getNano() == 0) {
+ return String.format("-%02d:%02d:%02d", hours, minutes, remaining);
+ }
+ return String.format("-%02d:%02d:%02d.%s", hours, minutes, remaining, Integer.parseInt(trimEnd(String.valueOf(duration.getNano()), '0')));
+ }
+
+ public static String trimEnd(String str, char trimChar) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+ char[] val = str.toCharArray();
+ int len = val.length;
+ while (0 < len && val[len - 1] == trimChar) {
+ len--;
+ }
+ return len < val.length ? str.substring(0, len) : str;
+ }
+
+ public static byte[] numberToBinaryArray(Number number) {
+ BigInteger bigInt = BigInteger.valueOf(number.longValue());
+ int size = (bigInt.bitLength() + 7) / 8;
+ byte[] result = new byte[size];
+ byte[] bigIntBytes = bigInt.toByteArray();
+ int start = bigInt.bitLength() % 8 == 0 ? 1 : 0;
+ int length = Math.min(bigIntBytes.length - start, size);
+ System.arraycopy(bigIntBytes, start, result, size - length, length);
+ return result;
+ }
+
+ public static Integer toInt(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strValue = ((String) value).toLowerCase();
+ if (StringUtils.isBlank(strValue)) {
+ return null;
+ }
+ try {
+ return Integer.parseInt(strValue);
+ } catch (Exception e) {
+ try {
+ return Integer.decode(strValue);
+ } catch (Exception e2) {
+ if ("true".equals(strValue)) {
+ return 1;
+ }
+ if ("false".equals(strValue)) {
+ return 0;
+ }
+ return new BigDecimal(strValue).intValue();
+ }
+ }
+ } else if (value instanceof Number) {
+ return ((Number) value).intValue();
+ } else {
+ if (value instanceof Boolean) {
+ return Boolean.TRUE.equals(value) ? 1 : 0;
+ }
+ throw new UnsupportedOperationException("class " + value.getClass() + ", value '" + value + "' , parse to int failed.");
+ }
+ }
+
+ private static LocalDateTime toLocalDateTime(String value) {
+ if (value.trim().length() >= 4) {
+ String dateStr2 = removeZone(value);
+ int len = dateStr2.length();
+ if (len == 4) {
+ return LocalDateTime.of(Integer.parseInt(dateStr2), 1, 1, 0, 0, 0, 0);
+ }
+ if (dateStr2.charAt(4) == '-') {
+ switch (len) {
+ case 7:
+ String[] dataParts = dateStr2.split("-");
+ return LocalDateTime.of(Integer.parseInt(dataParts[0]), Integer.parseInt(dataParts[1]), 1, 0, 0, 0, 0);
+ case 8:
+ case 9:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 17:
+ case 18:
+ default:
+ String[] dataTime = dateStr2.split(" ");
+ String[] dataParts2 = dataTime[0].split("-");
+ String[] timeParts = dataTime[1].split(":");
+ String[] secondParts = timeParts[2].split("\\.");
+ secondParts[1] = StringUtils.rightPad(secondParts[1], 9, Constants.CJ_MINOR_VERSION);
+ return LocalDateTime.of(Integer.parseInt(dataParts2[0]), Integer.parseInt(dataParts2[1]), Integer.parseInt(dataParts2[2]),
+ Integer.parseInt(timeParts[0]), Integer.parseInt(timeParts[1]), Integer.parseInt(secondParts[0]),
+ Integer.parseInt(secondParts[1]));
+ case 10:
+ String[] dataParts3 = dateStr2.split("-");
+ return LocalDateTime.of(Integer.parseInt(dataParts3[0]), Integer.parseInt(dataParts3[1]), Integer.parseInt(dataParts3[2]), 0,
+ 0, 0, 0);
+ case 13:
+ String[] dataTime2 = dateStr2.split(" ");
+ String[] dataParts4 = dataTime2[0].split("-");
+ return LocalDateTime.of(Integer.parseInt(dataParts4[0]), Integer.parseInt(dataParts4[1]), Integer.parseInt(dataParts4[2]),
+ Integer.parseInt(dataTime2[1]), 0, 0, 0);
+ case 16:
+ String[] dataTime3 = dateStr2.split(" ");
+ String[] dataParts5 = dataTime3[0].split("-");
+ String[] timeParts2 = dataTime3[1].split(":");
+ return LocalDateTime.of(Integer.parseInt(dataParts5[0]), Integer.parseInt(dataParts5[1]), Integer.parseInt(dataParts5[2]),
+ Integer.parseInt(timeParts2[0]), Integer.parseInt(timeParts2[1]), 0, 0);
+ case 19:
+ String[] dataTime4 = dateStr2.split(" ");
+ String[] dataParts6 = dataTime4[0].split("-");
+ String[] timeParts3 = dataTime4[1].split(":");
+ return LocalDateTime.of(Integer.parseInt(dataParts6[0]), Integer.parseInt(dataParts6[1]), Integer.parseInt(dataParts6[2]),
+ Integer.parseInt(timeParts3[0]), Integer.parseInt(timeParts3[1]), Integer.parseInt(timeParts3[2]), 0);
+ }
+ } else if (dateStr2.charAt(2) == ':') {
+ switch (len) {
+ case 5:
+ String[] timeParts4 = dateStr2.split(":");
+ return LocalDateTime.of(0, 1, 1, Integer.parseInt(timeParts4[0]), Integer.parseInt(timeParts4[1]), 0, 0);
+ case 8:
+ String[] timeParts5 = dateStr2.split(":");
+ return LocalDateTime.of(0, 1, 1, Integer.parseInt(timeParts5[0]), Integer.parseInt(timeParts5[1]),
+ Integer.parseInt(timeParts5[2]), 0);
+ default:
+ String[] timeParts6 = dateStr2.split(":");
+ String[] secondParts2 = timeParts6[2].split("\\.");
+ secondParts2[1] = StringUtils.rightPad(secondParts2[1], 9, Constants.CJ_MINOR_VERSION);
+ return LocalDateTime.of(0, 1, 1, Integer.parseInt(timeParts6[0]), Integer.parseInt(timeParts6[1]),
+ Integer.parseInt(secondParts2[0]), Integer.parseInt(secondParts2[1]));
+ }
+ } else {
+ throw new UnsupportedOperationException(value.getClass() + ", value '" + value + "' , parse to local date time failed.");
+ }
+ } else if (StringUtils.isNumeric(value)) {
+ return LocalDateTime.of(Integer.parseInt(value), 1, 1, 0, 0, 0, 0);
+ } else {
+ throw new DateTimeException(value + " format error.");
+ }
+ }
+
+ public static LocalDateTime toLocalDateTime(Object value) {
+ if (value == null || StringUtils.isBlank(value.toString())) {
+ return null;
+ }
+ if (value instanceof Temporal) {
+ if (value instanceof LocalDateTime) {
+ return (LocalDateTime) value;
+ }
+ if (value instanceof OffsetDateTime) {
+ return ((OffsetDateTime) value).toLocalDateTime();
+ }
+ if (value instanceof LocalTime) {
+ return LocalDateTime.of(LocalDate.of(1970, 1, 1), (LocalTime) value);
+ } else if (value instanceof LocalDate) {
+ return LocalDateTime.of((LocalDate) value, LocalTime.of(0, 0));
+ } else {
+ throw new UnsupportedOperationException(value.getClass() + ", value '" + value + "' , parse local date time failed.");
+ }
+ } else if (!(value instanceof java.util.Date)) {
+ return toLocalDateTime(value.toString());
+ } else {
+ if (value instanceof Timestamp) {
+ long nanos = ((Timestamp) value).getNanos();
+ return Instant.ofEpochMilli(((Timestamp) value).getTime() - (nanos / 1000000)).plusNanos(nanos).atZone(ZoneId.systemDefault())
+ .toLocalDateTime();
+ } else if (value instanceof java.sql.Date) {
+ return ((java.sql.Date) value).toLocalDate().atTime(0, 0);
+ } else {
+ if (!(value instanceof Time)) {
+ return ((java.util.Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
+ return LocalDateTime.of(LocalDate.of(1970, 1, 1),
+ Instant.ofEpochMilli(((Time) value).getTime()).atZone(ZoneId.systemDefault()).toLocalTime());
+ }
+ }
+ }
+
+ public static boolean isHexNumber(String str) {
+ boolean flag = true;
+ if (str.startsWith("0x") || str.startsWith("0X")) {
+ str = str.substring(2);
+ }
+ int i = 0;
+ while (true) {
+ if (i < str.length()) {
+ char cc = str.charAt(i);
+ if (cc != '0' && cc != '1' && cc != '2' && cc != '3' && cc != '4' && cc != '5' && cc != '6' && cc != '7' && cc != '8' && cc != '9'
+ && cc != 'A' && cc != 'B' && cc != 'C' && cc != 'D' && cc != 'E' && cc != 'F' && cc != 'a' && cc != 'b' && cc != 'c' && cc != 'd'
+ && cc != 'e' && cc != 'f') {
+ flag = false;
+ break;
+ }
+ i++;
+ } else {
+ break;
+ }
+ }
+ return flag;
+ }
+
+ public static byte[] toBytes(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strVal = (String) value;
+ if ((strVal.startsWith("0x") || strVal.startsWith("0X")) && isHexNumber(strVal)) {
+ return hex2bytes(strVal.substring(2));
+ }
+ return ((String) value).getBytes(StandardCharsets.ISO_8859_1);
+ } else if (value instanceof byte[]) {
+ return (byte[]) value;
+ } else {
+ throw new UnsupportedOperationException("class " + value.getClass() + ", value '" + value + "' , parse to bytes failed.");
+ }
+ }
+
+ public static String toGeometry(Object value) throws Exception {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ String strVal = (String) value;
+ if (!strVal.startsWith("0x") && !strVal.startsWith("0X")) {
+ return (String) value;
+ }
+ return new WKTReader().read((String) value).toText();
+ } else if (value instanceof byte[]) {
+ // mysql add 4 byte in header of geometry
+ byte[] bytes = (byte[]) value;
+ if (bytes.length > 4) {
+ byte[] dst = new byte[bytes.length - 4];
+ System.arraycopy(bytes, 4, dst, 0, bytes.length - 4);
+ return new WKBReader().read(dst).toText();
+ }
+ return new WKBReader().read(bytes).toText();
+ } else {
+ throw new UnsupportedOperationException("class " + value.getClass() + ", value '" + value + "' , " + "parse to geometry failed.");
+ }
+ }
+
+ public static byte[] hex2bytes(String hexStr) {
+ if (hexStr == null) {
+ return null;
+ }
+ if (org.apache.commons.lang3.StringUtils.isBlank(hexStr)) {
+ return new byte[0];
+ }
+
+ if (hexStr.length() % 2 == 1) {
+ hexStr = "0" + hexStr;
+ }
+
+ int count = hexStr.length() / 2;
+ byte[] ret = new byte[count];
+ for (int i = 0; i < count; i++) {
+ int index = i * 2;
+ char c1 = hexStr.charAt(index);
+ char c2 = hexStr.charAt(index + 1);
+ ret[i] = (byte) (toByte(c1) << 4);
+ ret[i] = (byte) (ret[i] | toByte(c2));
+ }
+ return ret;
+ }
+
+ private static byte toByte(char src) {
+ switch (Character.toUpperCase(src)) {
+ case '0':
+ return 0;
+ case '1':
+ return 1;
+ case '2':
+ return 2;
+ case '3':
+ return 3;
+ case '4':
+ return 4;
+ case '5':
+ return 5;
+ case '6':
+ return 6;
+ case '7':
+ return 7;
+ case '8':
+ return 8;
+ case '9':
+ return 9;
+ case 'A':
+ return 10;
+ case 'B':
+ return 11;
+ case 'C':
+ return 12;
+ case 'D':
+ return 13;
+ case 'E':
+ return 14;
+ case 'F':
+ return 15;
+ default:
+ throw new IllegalStateException("0-F");
+ }
}
}
diff --git a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkConnector.java b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkConnector.java
index 1888e204ac..8f9df7595b 100644
--- a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkConnector.java
+++ b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkConnector.java
@@ -31,6 +31,7 @@
import org.apache.eventmesh.connector.canal.sink.DbLoadData;
import org.apache.eventmesh.connector.canal.sink.DbLoadData.TableLoadData;
import org.apache.eventmesh.connector.canal.sink.DbLoadMerger;
+import org.apache.eventmesh.connector.canal.source.table.RdbTableMgr;
import org.apache.eventmesh.openconnect.api.ConnectorCreateService;
import org.apache.eventmesh.openconnect.api.connector.ConnectorContext;
import org.apache.eventmesh.openconnect.api.connector.SinkConnectorContext;
@@ -88,6 +89,7 @@ public class CanalSinkConnector implements Sink, ConnectorCreateService {
private int batchSize = 50;
private boolean useBatch = true;
+ private RdbTableMgr tableMgr;
@Override
public Class extends Config> configClass() {
@@ -107,12 +109,13 @@ public void init(ConnectorContext connectorContext) throws Exception {
this.sinkConfig = (CanalSinkConfig) sinkConnectorContext.getSinkConfig();
this.batchSize = sinkConfig.getBatchSize();
this.useBatch = sinkConfig.getUseBatch();
- DatabaseConnection.sinkConfig = this.sinkConfig;
+ DatabaseConnection.sinkConfig = this.sinkConfig.getSinkConnectorConfig();
DatabaseConnection.initSinkConnection();
jdbcTemplate = new JdbcTemplate(DatabaseConnection.sinkDataSource);
dbDialect = new MysqlDialect(jdbcTemplate, new DefaultLobHandler());
interceptor = new SqlBuilderLoadInterceptor();
interceptor.setDbDialect(dbDialect);
+ tableMgr = new RdbTableMgr(sinkConfig.getSinkConnectorConfig(), DatabaseConnection.sinkDataSource);
executor = new ThreadPoolExecutor(sinkConfig.getPoolSize(),
sinkConfig.getPoolSize(),
0L,
@@ -124,7 +127,7 @@ public void init(ConnectorContext connectorContext) throws Exception {
@Override
public void start() throws Exception {
-
+ tableMgr.start();
}
@Override
@@ -147,7 +150,7 @@ public void put(List sinkRecords) {
DbLoadContext context = new DbLoadContext();
for (ConnectRecord connectRecord : sinkRecords) {
List canalConnectRecordList = (List) connectRecord.getData();
- canalConnectRecordList = filterRecord(canalConnectRecordList, sinkConfig);
+ canalConnectRecordList = filterRecord(canalConnectRecordList);
if (isDdlDatas(canalConnectRecordList)) {
doDdl(context, canalConnectRecordList);
} else {
@@ -179,10 +182,9 @@ private boolean isDdlDatas(List canalConnectRecordList) {
return result;
}
- private List filterRecord(List canalConnectRecordList, CanalSinkConfig sinkConfig) {
+ private List filterRecord(List canalConnectRecordList) {
return canalConnectRecordList.stream()
- .filter(record -> sinkConfig.getSinkConnectorConfig().getSchemaName().equalsIgnoreCase(record.getSchemaName())
- && sinkConfig.getSinkConnectorConfig().getTableName().equalsIgnoreCase(record.getTableName()))
+ .filter(record -> tableMgr.getTable(record.getSchemaName(), record.getTableName()) != null)
.collect(Collectors.toList());
}
diff --git a/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkFullConnector.java b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkFullConnector.java
new file mode 100644
index 0000000000..36c03b156c
--- /dev/null
+++ b/eventmesh-connectors/eventmesh-connector-canal/src/main/java/org/apache/eventmesh/connector/canal/sink/connector/CanalSinkFullConnector.java
@@ -0,0 +1,401 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.eventmesh.connector.canal.sink.connector;
+
+import org.apache.eventmesh.common.config.connector.Config;
+import org.apache.eventmesh.common.config.connector.rdb.canal.CanalSinkFullConfig;
+import org.apache.eventmesh.common.config.connector.rdb.canal.mysql.Constants;
+import org.apache.eventmesh.common.config.connector.rdb.canal.mysql.MySQLColumnDef;
+import org.apache.eventmesh.common.config.connector.rdb.canal.mysql.MySQLTableDef;
+import org.apache.eventmesh.common.exception.EventMeshException;
+import org.apache.eventmesh.common.remote.offset.canal.CanalFullRecordOffset;
+import org.apache.eventmesh.connector.canal.DatabaseConnection;
+import org.apache.eventmesh.connector.canal.SqlUtils;
+import org.apache.eventmesh.connector.canal.source.table.RdbTableMgr;
+import org.apache.eventmesh.openconnect.api.ConnectorCreateService;
+import org.apache.eventmesh.openconnect.api.connector.ConnectorContext;
+import org.apache.eventmesh.openconnect.api.connector.SinkConnectorContext;
+import org.apache.eventmesh.openconnect.api.sink.Sink;
+import org.apache.eventmesh.openconnect.offsetmgmt.api.data.ConnectRecord;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.LockSupport;
+
+import com.alibaba.druid.pool.DruidPooledConnection;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CanalSinkFullConnector implements Sink, ConnectorCreateService {
+ private CanalSinkFullConfig config;
+ private RdbTableMgr tableMgr;
+ private final DateTimeFormatter dataTimePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");
+
+ @Override
+ public void start() throws Exception {
+ tableMgr.start();
+ }
+
+ @Override
+ public void stop() throws Exception {
+
+ }
+
+ @Override
+ public Sink create() {
+ return new CanalSinkFullConnector();
+ }
+
+ @Override
+ public Class extends Config> configClass() {
+ return CanalSinkFullConfig.class;
+ }
+
+ @Override
+ public void init(Config config) throws Exception {
+ this.config = (CanalSinkFullConfig) config;
+ init();
+ }
+
+ @Override
+ public void init(ConnectorContext connectorContext) throws Exception {
+ this.config = (CanalSinkFullConfig) ((SinkConnectorContext) connectorContext).getSinkConfig();
+ init();
+ }
+
+ private void init() {
+ if (config.getSinkConfig() == null) {
+ throw new EventMeshException(String.format("[%s] sink config is null", this.getClass()));
+ }
+ DatabaseConnection.sinkConfig = this.config.getSinkConfig();
+ DatabaseConnection.initSinkConnection();
+ DatabaseConnection.sinkDataSource.setDefaultAutoCommit(false);
+
+ tableMgr = new RdbTableMgr(this.config.getSinkConfig(), DatabaseConnection.sinkDataSource);
+ }
+
+ @Override
+ public void commit(ConnectRecord record) {
+
+ }
+
+ @Override
+ public String name() {
+ return null;
+ }
+
+ @Override
+ public void put(List sinkRecords) {
+ if (sinkRecords == null || sinkRecords.isEmpty() || sinkRecords.get(0) == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("[{}] got sink records are none", this.getClass());
+ }
+ return;
+ }
+ ConnectRecord record = sinkRecords.get(0);
+ List