diff --git a/client-launcher/pom.xml b/client-launcher/pom.xml
new file mode 100644
index 00000000..42d605a7
--- /dev/null
+++ b/client-launcher/pom.xml
@@ -0,0 +1,135 @@
+
+
+
+ canal
+ com.alibaba.otter
+ 1.1.0-SNAPSHOT
+
+ 4.0.0
+ com.alibaba.otter
+ canal.client-launcher
+ jar
+ canal client launcher module for otter ${project.version}
+
+
+ com.alibaba.otter
+ canal.client
+ ${project.version}
+
+
+
+ com.alibaba.otter
+ canal.kafka.client
+ ${project.version}
+
+
+ joda-time
+ joda-time
+ 2.9.4
+
+
+ org.yaml
+ snakeyaml
+ 1.17
+
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+ true
+
+
+ **/logback.xml
+ **/canal-client.yml
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+ 2.2.1
+
+
+ assemble
+
+ single
+
+ package
+
+
+
+ false
+ false
+
+
+
+
+
+
+
+ dev
+
+ true
+
+ env
+ !release
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+
+ ${basedir}/src/main/assembly/dev.xml
+
+ canal_client
+ ${project.build.directory}
+
+
+
+
+
+
+
+
+ release
+
+
+ env
+ release
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+
+ ${basedir}/src/main/assembly/release.xml
+
+
+ ${project.artifactId}-${project.version}
+
+ ${project.parent.build.directory}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client-launcher/src/main/assembly/dev.xml b/client-launcher/src/main/assembly/dev.xml
new file mode 100644
index 00000000..a3cbc20c
--- /dev/null
+++ b/client-launcher/src/main/assembly/dev.xml
@@ -0,0 +1,57 @@
+
+ dist
+
+ dir
+
+ false
+
+
+ .
+ /
+
+ README*
+
+
+
+ ./src/main/bin
+ bin
+
+ **/*
+
+ 0755
+
+
+ ./src/main/conf
+ /conf
+
+ **/*
+
+
+
+ ./src/main/resources
+ /conf
+
+ **/*
+
+
+ META-INF/**
+
+
+
+ target
+ logs
+
+ **/*
+
+
+
+
+
+ lib
+
+ junit:junit
+
+
+
+
diff --git a/client-launcher/src/main/assembly/release.xml b/client-launcher/src/main/assembly/release.xml
new file mode 100644
index 00000000..9350da6e
--- /dev/null
+++ b/client-launcher/src/main/assembly/release.xml
@@ -0,0 +1,57 @@
+
+ dist
+
+ tar.gz
+
+ false
+
+
+ .
+ /
+
+ README*
+
+
+
+ ./src/main/bin
+ bin
+
+ **/*
+
+ 0755
+
+
+ ./src/main/conf
+ /conf
+
+ **/*
+
+
+
+ ./src/main/resources
+ /conf
+
+ **/*
+
+
+ META-INF/**
+
+
+
+ target
+ logs
+
+ **/*
+
+
+
+
+
+ lib
+
+ junit:junit
+
+
+
+
diff --git a/client-launcher/src/main/bin/startup.bat b/client-launcher/src/main/bin/startup.bat
new file mode 100644
index 00000000..33fd7945
--- /dev/null
+++ b/client-launcher/src/main/bin/startup.bat
@@ -0,0 +1,25 @@
+@echo off
+@if not "%ECHO%" == "" echo %ECHO%
+@if "%OS%" == "Windows_NT" setlocal
+
+set ENV_PATH=.\
+if "%OS%" == "Windows_NT" set ENV_PATH=%~dp0%
+
+set conf_dir=%ENV_PATH%\..\conf
+set canal_conf=%conf_dir%\canal-client.yml
+set logback_configurationFile=%conf_dir%\logback.xml
+
+set CLASSPATH=%conf_dir%
+set CLASSPATH=%conf_dir%\..\lib\*;%CLASSPATH%
+
+set JAVA_MEM_OPTS= -Xms128m -Xmx512m -XX:PermSize=128m
+set JAVA_OPTS_EXT= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dapplication.codeset=UTF-8 -Dfile.encoding=UTF-8
+set JAVA_DEBUG_OPT= -server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9099,server=y,suspend=n
+set CANAL_OPTS= -DappName=otter-canal -Dlogback.configurationFile="%logback_configurationFile%" -Dcanal.conf="%canal_conf%"
+
+set JAVA_OPTS= %JAVA_MEM_OPTS% %JAVA_OPTS_EXT% %JAVA_DEBUG_OPT% %CANAL_OPTS%
+
+set CMD_STR= java %JAVA_OPTS% -classpath "%CLASSPATH%" java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.client.ClientLauncher
+echo start cmd : %CMD_STR%
+
+java %JAVA_OPTS% -classpath "%CLASSPATH%" com.alibaba.otter.canal.client.ClientLauncher
\ No newline at end of file
diff --git a/client-launcher/src/main/bin/startup.sh b/client-launcher/src/main/bin/startup.sh
new file mode 100644
index 00000000..d467b4d2
--- /dev/null
+++ b/client-launcher/src/main/bin/startup.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+
+current_path=`pwd`
+case "`uname`" in
+ Linux)
+ bin_abs_path=$(readlink -f $(dirname $0))
+ ;;
+ *)
+ bin_abs_path=`cd $(dirname $0); pwd`
+ ;;
+esac
+base=${bin_abs_path}/..
+canal_conf=$base/conf/canal-client.yml
+logback_configurationFile=$base/conf/logback.xml
+export LANG=en_US.UTF-8
+export BASE=$base
+
+if [ -f $base/bin/canal_client.pid ] ; then
+ echo "found canal_client.pid , Please run stop.sh first ,then startup.sh" 2>&2
+ exit 1
+fi
+
+## set java path
+if [ -z "$JAVA" ] ; then
+ JAVA=$(which java)
+fi
+
+ALIBABA_JAVA="/usr/alibaba/java/bin/java"
+TAOBAO_JAVA="/opt/taobao/java/bin/java"
+if [ -z "$JAVA" ]; then
+ if [ -f $ALIBABA_JAVA ] ; then
+ JAVA=$ALIBABA_JAVA
+ elif [ -f $TAOBAO_JAVA ] ; then
+ JAVA=$TAOBAO_JAVA
+ else
+ echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." 2>&2
+ exit 1
+ fi
+fi
+
+case "$#"
+in
+0 )
+ ;;
+1 )
+ var=$*
+ if [ -f $var ] ; then
+ canal_conf=$var
+ else
+ echo "THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN."
+ exit
+ fi;;
+2 )
+ var=$1
+ if [ -f $var ] ; then
+ canal_conf=$var
+ else
+ if [ "$1" = "debug" ]; then
+ DEBUG_PORT=$2
+ DEBUG_SUSPEND="n"
+ JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
+ fi
+ fi;;
+* )
+ echo "THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN."
+ exit;;
+esac
+
+str=`file -L $JAVA | grep 64-bit`
+if [ -n "$str" ]; then
+ JAVA_OPTS="-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError"
+else
+ JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m "
+fi
+
+JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
+CANAL_OPTS="-DappName=otter-canal -Dlogback.configurationFile=$logback_configurationFile -Dcanal.conf=$canal_conf"
+
+if [ -e $canal_conf -a -e $logback_configurationFile ]
+then
+
+ for i in $base/lib/*;
+ do CLASSPATH=$i:"$CLASSPATH";
+ done
+ CLASSPATH="$base/conf:$CLASSPATH";
+
+ echo "cd to $bin_abs_path for workaround relative path"
+ cd $bin_abs_path
+
+ echo LOG CONFIGURATION : $logback_configurationFile
+ echo canal conf : $canal_conf
+ echo CLASSPATH :$CLASSPATH
+ $JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.client.ClientLauncher 1>>$base/logs/canal_client.log 2>&1 &
+ echo $! > $base/bin/canal_client.pid
+
+ echo "cd to $current_path for continue"
+ cd $current_path
+else
+ echo "canal client conf("$canal_conf") OR log configration file($logback_configurationFile) is not exist,please create then first!"
+fi
diff --git a/client-launcher/src/main/bin/stop.sh b/client-launcher/src/main/bin/stop.sh
new file mode 100644
index 00000000..0cfcc712
--- /dev/null
+++ b/client-launcher/src/main/bin/stop.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+cygwin=false;
+linux=false;
+case "`uname`" in
+ CYGWIN*)
+ cygwin=true
+ ;;
+ Linux*)
+ linux=true
+ ;;
+esac
+
+get_pid() {
+ STR=$1
+ PID=$2
+ if $cygwin; then
+ JAVA_CMD="$JAVA_HOME\bin\java"
+ JAVA_CMD=`cygpath --path --unix $JAVA_CMD`
+ JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`
+ else
+ if $linux; then
+ if [ ! -z "$PID" ]; then
+ JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
+ else
+ JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep -v grep|awk '{print $2}'`
+ fi
+ else
+ if [ ! -z "$PID" ]; then
+ JAVA_PID=`ps aux |grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
+ else
+ JAVA_PID=`ps aux |grep "$STR"|grep -v grep|awk '{print $2}'`
+ fi
+ fi
+ fi
+ echo $JAVA_PID;
+}
+
+base=`dirname $0`/..
+pidfile=$base/bin/canal_client.pid
+if [ ! -f "$pidfile" ];then
+ echo "canal client is not running. exists"
+ exit
+fi
+
+pid=`cat $pidfile`
+if [ "$pid" == "" ] ; then
+ pid=`get_pid "appName=otter-canal"`
+fi
+
+echo -e "`hostname`: stopping canal $pid ... "
+kill $pid
+
+LOOPS=0
+while (true);
+do
+ gpid=`get_pid "appName=otter-canal" "$pid"`
+ if [ "$gpid" == "" ] ; then
+ echo "Oook! cost:$LOOPS"
+ `rm $pidfile`
+ break;
+ fi
+ let LOOPS=LOOPS+1
+ sleep 1
+done
\ No newline at end of file
diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/CanalAdapterLoader.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/CanalAdapterLoader.java
new file mode 100644
index 00000000..f60a830e
--- /dev/null
+++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/CanalAdapterLoader.java
@@ -0,0 +1,160 @@
+package com.alibaba.otter.canal.client;
+
+import com.alibaba.otter.canal.client.adapter.CanalAdapterKafkaWorker;
+import com.alibaba.otter.canal.client.adapter.CanalAdapterWorker;
+import com.alibaba.otter.canal.client.adapter.CanalOuterAdapter;
+import com.alibaba.otter.canal.client.adapter.CanalOuterAdapterConfiguration;
+import com.alibaba.otter.canal.client.support.CanalClientConfig;
+import com.alibaba.otter.canal.client.support.ExtensionLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * 外部适配器的加载器
+ *
+ * @author machengyuan 2018-8-19 下午11:45:49
+ * @version 1.0.0
+ */
+public class CanalAdapterLoader {
+ private static final Logger logger = LoggerFactory.getLogger(CanalAdapterLoader.class);
+
+ private CanalClientConfig canalClientConfig;
+
+ private Map canalWorkers = new HashMap<>();
+
+ private Map canalKafkaWorkers = new HashMap<>();
+
+ private ExtensionLoader loader;
+
+ public CanalAdapterLoader(CanalClientConfig canalClientConfig) {
+ this.canalClientConfig = canalClientConfig;
+ }
+
+ /**
+ * 初始化canal-client、 canal-client-kafka的适配器
+ */
+ public void init() {
+ // canal instances 和 kafka topics 配置不能同时为空
+ if (canalClientConfig.getCanalInstances().isEmpty() && canalClientConfig.getKafkaTopics().isEmpty()) {
+ throw new RuntimeException("Blank config property: canalInstances or canalKafkaTopics");
+ }
+
+ loader = ExtensionLoader.getExtensionLoader(CanalOuterAdapter.class, "" /*TODO canalClientConfig.getClassloaderPolicy()*/);
+
+ String canalServerHost = this.canalClientConfig.getCanalServerHost();
+ SocketAddress sa = null;
+ if (canalServerHost != null) {
+ String[] ipPort = canalServerHost.split(":");
+ sa = new InetSocketAddress(ipPort[0], Integer.parseInt(ipPort[1]));
+ }
+ String zkHosts = this.canalClientConfig.getZookeeperHosts();
+
+ if (zkHosts == null && sa == null) {
+ throw new RuntimeException("Blank config property: canalServerHost or zookeeperHosts");
+ }
+
+ // 初始化canal-client的适配器
+ for (CanalClientConfig.CanalInstance instance : canalClientConfig.getCanalInstances()) {
+ List> canalOuterAdapterGroups = new ArrayList<>();
+
+ for (CanalClientConfig.AdapterGroup connectorGroup : instance.getAdapterGroups()) {
+ List canalOutConnectors = new ArrayList<>();
+ for (CanalOuterAdapterConfiguration c : connectorGroup.getOutAdapters()) {
+ loadConnector(c, canalOutConnectors);
+ }
+ canalOuterAdapterGroups.add(canalOutConnectors);
+ }
+ CanalAdapterWorker worker;
+ if (zkHosts != null) {
+ worker = new CanalAdapterWorker(instance.getInstance(), zkHosts, canalOuterAdapterGroups);
+ } else {
+ worker = new CanalAdapterWorker(instance.getInstance(), sa, canalOuterAdapterGroups);
+ }
+ canalWorkers.put(instance.getInstance(), worker);
+ worker.start();
+ logger.info("Start adapter for canal instance: {} succeed", instance.getInstance());
+ }
+
+ // 初始化canal-client-kafka的适配器
+ for (CanalClientConfig.KafkaTopic kafkaTopic : canalClientConfig.getKafkaTopics()) {
+ for (CanalClientConfig.Group group : kafkaTopic.getGroups()) {
+ List> canalOuterAdapterGroups = new ArrayList<>();
+
+ List canalOuterAdapters = new ArrayList<>();
+
+ for (CanalOuterAdapterConfiguration config : group.getOutAdapters()) {
+// for (CanalOuterAdapterConfiguration config : adaptor.getOutAdapters()) {
+ loadConnector(config, canalOuterAdapters);
+// }
+ }
+ canalOuterAdapterGroups.add(canalOuterAdapters);
+
+ String zkServers = canalClientConfig.getZookeeperHosts();
+ CanalAdapterKafkaWorker canalKafkaWorker = new CanalAdapterKafkaWorker(zkServers,
+ canalClientConfig.getBootstrapServers(),
+ kafkaTopic.getTopic(), group.getGroupId(), canalOuterAdapterGroups);
+ canalKafkaWorkers.put(kafkaTopic.getTopic() + "-" + group.getGroupId(), canalKafkaWorker);
+ canalKafkaWorker.start();
+ logger.info("Start adapter for canal-client kafka topic: {} succeed", kafkaTopic.getTopic() + "-" + group.getGroupId());
+ }
+ }
+ }
+
+ private void loadConnector(CanalOuterAdapterConfiguration config, List canalOutConnectors) {
+ try {
+ CanalOuterAdapter adapter = loader.getExtension(config.getName());
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ // 替换ClassLoader
+ Thread.currentThread().setContextClassLoader(adapter.getClass().getClassLoader());
+ adapter.init(config);
+ Thread.currentThread().setContextClassLoader(cl);
+ canalOutConnectors.add(adapter);
+ logger.info("Load canal adapter: {} succeed", config.getName());
+ } catch (Exception e) {
+ logger.error("Load canal adapter: {} failed", config.getName(), e);
+ }
+ }
+
+ /**
+ * 销毁所有适配器
+ * 为防止canal实例太多造成销毁阻塞, 并行销毁
+ */
+ public void destroy() {
+ if (canalWorkers.size() > 0) {
+ ExecutorService stopExecutorService = Executors.newFixedThreadPool(canalWorkers.size());
+ for (CanalAdapterWorker v : canalWorkers.values()) {
+ final CanalAdapterWorker caw = v;
+ stopExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ caw.stop();
+ }
+ });
+ }
+ stopExecutorService.shutdown();
+ }
+ if (canalKafkaWorkers.size() > 0) {
+ ExecutorService stopKafkaExecutorService = Executors.newFixedThreadPool(canalKafkaWorkers.size());
+ for (CanalAdapterKafkaWorker v : canalKafkaWorkers.values()) {
+ final CanalAdapterKafkaWorker cakw = v;
+ stopKafkaExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ cakw.stop();
+ }
+ });
+ }
+ stopKafkaExecutorService.shutdown();
+ }
+ logger.info("All canal adapters destroyed");
+ }
+}
diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java
new file mode 100644
index 00000000..556910dd
--- /dev/null
+++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/ClientLauncher.java
@@ -0,0 +1,63 @@
+package com.alibaba.otter.canal.client;
+
+import com.alibaba.otter.canal.client.support.CanalClientConfig;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.FileInputStream;
+
+public class ClientLauncher {
+ private static final String CLASSPATH_URL_PREFIX = "classpath:";
+ private static final Logger logger = LoggerFactory.getLogger(ClientLauncher.class);
+
+ public static void main(String[] args) {
+ try {
+ logger.info("## set default uncaught exception handler");
+ setGlobalUncaughtExceptionHandler();
+
+ logger.info("## load canal client configurations");
+ String conf = System.getProperty("client.conf", "classpath:canal-client.yml");
+ CanalClientConfig canalClientConfig;
+ if (conf.startsWith(CLASSPATH_URL_PREFIX)) {
+ conf = StringUtils.substringAfter(conf, CLASSPATH_URL_PREFIX);
+ canalClientConfig = new Yaml().loadAs(ClientLauncher.class.getClassLoader().getResourceAsStream(conf),
+ CanalClientConfig.class);
+ } else {
+ canalClientConfig = new Yaml().loadAs(new FileInputStream(conf), CanalClientConfig.class);
+ }
+ logger.info("## start the canal client adapters.");
+ final CanalAdapterLoader adapterLoader = new CanalAdapterLoader(canalClientConfig);
+ adapterLoader.init();
+ logger.info("## the canal client adapters are running now ......");
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+
+ public void run() {
+ try {
+ logger.info("## stop the canal client adapters");
+ adapterLoader.destroy();
+ } catch (Throwable e) {
+ logger.warn("## something goes wrong when stopping canal client adapters:", e);
+ } finally {
+ logger.info("## canal client adapters are down.");
+ }
+ }
+
+ });
+ } catch (Throwable e) {
+ logger.error("## something goes wrong when starting up the canal client adapters:", e);
+ System.exit(0);
+ }
+ }
+
+ private static void setGlobalUncaughtExceptionHandler() {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ logger.error("UnCaughtException", e);
+ }
+ });
+ }
+}
diff --git a/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/AbstractCanalAdapterWorker.java b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/AbstractCanalAdapterWorker.java
new file mode 100644
index 00000000..42a154d4
--- /dev/null
+++ b/client-launcher/src/main/java/com/alibaba/otter/canal/client/adapter/AbstractCanalAdapterWorker.java
@@ -0,0 +1,156 @@
+package com.alibaba.otter.canal.client.adapter;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.otter.canal.client.support.Dml;
+import com.alibaba.otter.canal.client.support.JdbcTypeUtil;
+import com.alibaba.otter.canal.protocol.CanalEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * 适配器工作线程抽象类
+ *
+ * @author machengyuan 2018-8-19 下午11:30:49
+ * @version 1.0.0
+ */
+public abstract class AbstractCanalAdapterWorker {
+ protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ protected String canalDestination; // canal实例
+ protected List> canalOuterAdapters; // 外部适配器
+ protected ExecutorService groupInnerExecutorService; // 组内工作线程池
+ protected volatile boolean running = false; // 是否运行中
+ protected Thread thread = null;
+ protected Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ logger.error("parse events has an error", e);
+ }
+ };
+
+ /**
+ * 将每个entry转换为DML操作对象并调用所有适配器写入
+ *
+ * @param entries
+ */
+ protected void convertAndWrite(List entries) {
+ for (CanalEntry.Entry entry : entries) {
+ if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
+ continue;
+ }
+
+ CanalEntry.RowChange rowChange;
+ try {
+ rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
+ } catch (Exception e) {
+ throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
+ }
+
+ CanalEntry.EventType eventType = rowChange.getEventType();
+
+ final Dml dml = new Dml();
+ dml.setCanalDestination(canalDestination);
+ dml.setDatabase(entry.getHeader().getSchemaName());
+ dml.setTable(entry.getHeader().getTableName());
+ dml.setType(eventType.toString());
+ dml.setTs(System.currentTimeMillis());
+ dml.setSql(rowChange.getSql());
+ List