[SCB-2084] ServiceComb support dtm (#1961)

* [SCB-2084] ServiceComb support dtm

* [SCB-2084] ServiceComb support dtm:add pom

* [SCB-2084] ServiceComb support dtm: modify as review
diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml
index b8e6e95..629ee3e 100644
--- a/dependencies/bom/pom.xml
+++ b/dependencies/bom/pom.xml
@@ -298,6 +298,12 @@
         <artifactId>servicestage</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <!-- ServiceComb Dtm extension -->
+      <dependency>
+        <groupId>org.apache.servicecomb</groupId>
+        <artifactId>dtm</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <!-- ServiceComb: swagger -->
       <dependency>
         <groupId>org.apache.servicecomb</groupId>
diff --git a/distribution/pom.xml b/distribution/pom.xml
index 544a39e..67ec88e 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -214,10 +214,10 @@
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>registry-local</artifactId>
     </dependency>
-      <dependency>
-        <groupId>org.apache.servicecomb</groupId>
-        <artifactId>registry-schema-discovery</artifactId>
-      </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>registry-schema-discovery</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
       <artifactId>registry-service-center</artifactId>
@@ -253,6 +253,12 @@
       <artifactId>servicestage</artifactId>
     </dependency>
 
+    <!-- ServiceComb dtm extension -->
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>dtm</artifactId>
+    </dependency>
+
     <!-- swagger -->
     <dependency>
       <groupId>org.apache.servicecomb</groupId>
diff --git a/huawei-cloud/dtm/pom.xml b/huawei-cloud/dtm/pom.xml
new file mode 100644
index 0000000..c1f3e9d
--- /dev/null
+++ b/huawei-cloud/dtm/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>huawei-cloud</artifactId>
+    <groupId>org.apache.servicecomb</groupId>
+    <version>2.1.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>dtm</artifactId>
+  <name>Java Chassis::Huawei Cloud::Dtm</name>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>java-chassis-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConfig.java b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConfig.java
new file mode 100644
index 0000000..5cf2828
--- /dev/null
+++ b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.huaweicloud.dtm;
+
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+
+public class DtmConfig {
+  private static final String DTM_CONTEXT_CLASS_NAME_KEY = "servicecomb.dtm.className";
+
+  private static final String DTM_CONTEXT_DEFAULT_CLASS_NAME = "com.huawei.middleware.dtm.client.context.DTMContext";
+
+  public static final String DTM_EXPORT_METHOD = "getContextData";
+
+  public static final String DTM_IMPORT_METHOD = "setContextData";
+
+  public static final String DTM_TRACE_ID_KEY = "X-Dtm-Trace-Id-Key";
+
+  public static final DtmConfig INSTANCE = new DtmConfig();
+
+  private DynamicStringProperty contextClassNameProperty = DynamicPropertyFactory.getInstance()
+      .getStringProperty(DTM_CONTEXT_CLASS_NAME_KEY, DTM_CONTEXT_DEFAULT_CLASS_NAME);
+
+  private DtmConfig() {
+  }
+
+  public String getDtmContextClassName() {
+    return contextClassNameProperty.get();
+  }
+}
diff --git a/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConsumerHandler.java b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConsumerHandler.java
new file mode 100644
index 0000000..c66d9b6
--- /dev/null
+++ b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmConsumerHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.huaweicloud.dtm;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Handler;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.MicroserviceMeta;
+import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DtmConsumerHandler implements Handler {
+  private static final Logger LOG = LoggerFactory.getLogger(DtmConsumerHandler.class);
+
+  private Method dtmContextExMethod;
+
+  @Override
+  public void init(MicroserviceMeta microserviceMeta, InvocationType invocationType) {
+    String className = DtmConfig.INSTANCE.getDtmContextClassName();
+    try {
+      Class<?> clazz = Class.forName(className);
+      dtmContextExMethod = clazz.getMethod(DtmConfig.DTM_EXPORT_METHOD);
+    } catch (Throwable e) {
+      // ignore just warn
+      LOG.warn("Failed to init method {}#{}", className, DtmConfig.DTM_EXPORT_METHOD, e);
+    }
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+    try {
+      if (dtmContextExMethod != null) {
+        Object context = dtmContextExMethod.invoke(null);
+        if (context instanceof Map) {
+          invocation.getContext().putAll((Map<? extends String, ? extends String>) context);
+        }
+      }
+    } catch (Throwable e) {
+      LOG.warn("Failed to execute method DTMContext#{}, please check", DtmConfig.DTM_EXPORT_METHOD, e);
+    }
+    invocation.next(asyncResp);
+  }
+}
diff --git a/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmProviderHandler.java b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmProviderHandler.java
new file mode 100644
index 0000000..0274beb
--- /dev/null
+++ b/huawei-cloud/dtm/src/main/java/org/apache/servicecomb/huaweicloud/dtm/DtmProviderHandler.java
@@ -0,0 +1,63 @@
+/*
+ * 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.servicecomb.huaweicloud.dtm;
+
+import static org.apache.servicecomb.huaweicloud.dtm.DtmConfig.DTM_TRACE_ID_KEY;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Handler;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.MicroserviceMeta;
+import org.apache.servicecomb.swagger.invocation.AsyncResponse;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DtmProviderHandler implements Handler {
+  private static final Logger LOG = LoggerFactory.getLogger(DtmProviderHandler.class);
+
+  private Method dtmContextImMethod;
+
+  @Override
+  public void init(MicroserviceMeta microserviceMeta, InvocationType invocationType) {
+    String className = DtmConfig.INSTANCE.getDtmContextClassName();
+    try {
+      Class<?> clazz = Class.forName(className);
+      dtmContextImMethod = clazz.getMethod(DtmConfig.DTM_IMPORT_METHOD, Map.class);
+    } catch (Throwable e) {
+      // ignore just warn
+      LOG.warn("Failed to init method {}#{}", className, DtmConfig.DTM_IMPORT_METHOD, e);
+    }
+  }
+
+  @Override
+  public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+    try {
+      if (dtmContextImMethod != null) {
+        String traceId = invocation.getContext().get(Const.TRACE_ID_NAME);
+        invocation.getContext().put(DTM_TRACE_ID_KEY, traceId);
+        dtmContextImMethod.invoke(null, invocation.getContext());
+      }
+    } catch (Throwable e) {
+      LOG.warn("Failed to execute method DTMContext#{}, please check", DtmConfig.DTM_IMPORT_METHOD, e);
+    }
+    invocation.next(asyncResp);
+  }
+}
diff --git a/huawei-cloud/dtm/src/main/resources/config/cse.handler.xml b/huawei-cloud/dtm/src/main/resources/config/cse.handler.xml
new file mode 100644
index 0000000..04009a1
--- /dev/null
+++ b/huawei-cloud/dtm/src/main/resources/config/cse.handler.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<config>
+  <handler id="dtm-consumer"
+    class="org.apache.servicecomb.huaweicloud.dtm.DtmConsumerHandler"/>
+  <handler id="dtm-provider"
+    class="org.apache.servicecomb.huaweicloud.dtm.DtmProviderHandler"/>
+</config>
diff --git a/huawei-cloud/dtm/src/test/java/com/huawei/middleware/dtm/client/context/DTMContext.java b/huawei-cloud/dtm/src/test/java/com/huawei/middleware/dtm/client/context/DTMContext.java
new file mode 100644
index 0000000..9930794
--- /dev/null
+++ b/huawei-cloud/dtm/src/test/java/com/huawei/middleware/dtm/client/context/DTMContext.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.huawei.middleware.dtm.client.context;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.util.CollectionUtils;
+
+public class DTMContext {
+  public static final String GLOBAL_TX_ID_KEY = "dtm-global-tx-key";
+
+  public static final String TRACE_ID_KEY = "X-Dtm-Trace-Id-Key";
+
+  public static String GLOBAL_TX_ID = "";
+
+  public static String TRACE_ID = "";
+
+  public static Map<String, String> getContextData() {
+    HashMap<String, String> context = new HashMap<>();
+    context.put(GLOBAL_TX_ID_KEY, GLOBAL_TX_ID);
+    return context;
+  }
+
+  public static void setContextData(Map<String, String> context) {
+    if (CollectionUtils.isEmpty(context)) {
+      return;
+    }
+    GLOBAL_TX_ID = context.getOrDefault(GLOBAL_TX_ID_KEY, "");
+    TRACE_ID = context.getOrDefault(TRACE_ID_KEY, "");
+  }
+}
diff --git a/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestConsumerHandler.java b/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestConsumerHandler.java
new file mode 100644
index 0000000..d078cd1
--- /dev/null
+++ b/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestConsumerHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.huaweicloud.dtm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Invocation;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.jupiter.api.BeforeAll;
+
+import com.huawei.middleware.dtm.client.context.DTMContext;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+public class TestConsumerHandler {
+
+  @BeforeClass
+  public static void init() {
+    DTMContext.TRACE_ID = "";
+    DTMContext.GLOBAL_TX_ID = "";
+  }
+
+  @Test
+  public void testHandler(@Mocked Invocation invocation) throws Exception {
+    DtmConsumerHandler consumerHandler = new DtmConsumerHandler();
+    consumerHandler.init(null, null);
+    Map<String, String> context = new HashMap<>();
+    new Expectations() {{
+      invocation.getContext();
+      result = context;
+    }};
+    String expectTxId = "dtm-tx-id";
+    String expectTraceId = "dtm-trace-id";
+
+    Assert.assertEquals(0, context.size());
+    DTMContext.GLOBAL_TX_ID = expectTxId;
+    DTMContext.TRACE_ID = expectTraceId;
+    consumerHandler.handle(invocation, null);
+    Assert.assertEquals(1, context.size());
+    Assert.assertEquals(expectTxId, context.get(DTMContext.GLOBAL_TX_ID_KEY));
+  }
+}
diff --git a/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestDtmProviderHandler.java b/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestDtmProviderHandler.java
new file mode 100644
index 0000000..0ca8480
--- /dev/null
+++ b/huawei-cloud/dtm/src/test/java/org/apache/servicecomb/huaweicloud/dtm/TestDtmProviderHandler.java
@@ -0,0 +1,61 @@
+/*
+ * 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.servicecomb.huaweicloud.dtm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Invocation;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.huawei.middleware.dtm.client.context.DTMContext;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+public class TestDtmProviderHandler {
+
+  @BeforeClass
+  public static void init() {
+    DTMContext.TRACE_ID = "";
+    DTMContext.GLOBAL_TX_ID = "";
+  }
+
+  @Test
+  public void testHandler(@Mocked Invocation invocation) throws Exception {
+    DtmProviderHandler providerHandler = new DtmProviderHandler();
+    providerHandler.init(null, null);
+    Map<String, String> context = new HashMap<>();
+    new Expectations() {{
+      invocation.getContext();
+      result = context;
+    }};
+    String expectTxId = "dtm-tx-id";
+    String expectTraceId = "dtm-trace-id";
+    context.put(DTMContext.GLOBAL_TX_ID_KEY, expectTxId);
+    context.put(Const.TRACE_ID_NAME, expectTraceId);
+
+    Assert.assertEquals("", DTMContext.TRACE_ID);
+    Assert.assertEquals("", DTMContext.GLOBAL_TX_ID);
+    providerHandler.handle(invocation, null);
+    Assert.assertEquals(expectTraceId, DTMContext.TRACE_ID);
+    Assert.assertEquals(expectTxId, DTMContext.GLOBAL_TX_ID);
+  }
+}
diff --git a/huawei-cloud/dtm/src/test/resources/log4j.properties b/huawei-cloud/dtm/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fbea91e
--- /dev/null
+++ b/huawei-cloud/dtm/src/test/resources/log4j.properties
@@ -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.
+#
+
+log4j.rootLogger=INFO, out, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.out.file=target/test.log
+log4j.appender.out.append=true
diff --git a/huawei-cloud/pom.xml b/huawei-cloud/pom.xml
index f5e56e0..06fffa0 100644
--- a/huawei-cloud/pom.xml
+++ b/huawei-cloud/pom.xml
@@ -32,6 +32,7 @@
 
   <modules>
     <module>servicestage</module>
+    <module>dtm</module>
   </modules>
 
 </project>
\ No newline at end of file