Repository: alibaba/fescar
Pull request #160 (2019-01-17 17:27:22)

Fix 3 problems of GlobalTransactionScanner class
1.Fix the problem of duplicate assign new instance of field 'interceptor'
2.Remove the unnecessary type parameter
3.Let static field 'PROXYED_SET' just contains the names of object of class that has methods annotated by @GlobalTransactional

<!-- Please make sure you have read and understood the contributing guidelines -->

### Ⅰ. Describe what this PR did


### Ⅱ. Does this pull request fix one issue?
<!-- If that, add "fixes #xxx" below in the next line, for example, fixes #97. -->


### Ⅲ. Why don't you add test cases (unit test/integration test)? 


### Ⅳ. Describe how to verify it


### Ⅴ. Special notes for reviews



codecov-io: # [Codecov](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=h1) Report
> Merging [#160](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=desc) into [develop](https://codecov.io/gh/alibaba/fescar/commit/7d57e0155e149188bb3962b3002e566382a13b22?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `0%`.

[![Impacted file tree graph](https://codecov.io/gh/alibaba/fescar/pull/160/graphs/tree.svg?width=650&token=Hy9a6MCc4p&height=150&src=pr)](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           develop    #160      +/-   ##
==========================================
- Coverage     4.63%   4.63%   -0.01%     
==========================================
  Files          184     184              
  Lines         6746    6747       +1     
  Branches       917     918       +1     
==========================================
  Hits           313     313              
- Misses        6415    6416       +1     
  Partials        18      18
```


| [Impacted Files](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...ar/spring/annotation/GlobalTransactionScanner.java](https://codecov.io/gh/alibaba/fescar/pull/160/diff?src=pr&el=tree#diff-c3ByaW5nL3NyYy9tYWluL2phdmEvY29tL2FsaWJhYmEvZmVzY2FyL3NwcmluZy9hbm5vdGF0aW9uL0dsb2JhbFRyYW5zYWN0aW9uU2Nhbm5lci5qYXZh) | `0% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=footer). Last update [7d57e01...1cea80f](https://codecov.io/gh/alibaba/fescar/pull/160?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/README.md b/README.md
index 8a3ed3b13..475aaf33e 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 [![Build Status](https://travis-ci.org/alibaba/fescar.svg?branch=develop)](https://travis-ci.org/alibaba/fescar)
 [![codecov](https://codecov.io/gh/alibaba/fescar/branch/develop/graph/badge.svg)](https://codecov.io/gh/alibaba/fescar)
 ![license](https://img.shields.io/github/license/alibaba/fescar.svg)
-[![](https://img.shields.io/twitter/follow/fescar.svg?label=Follow&style=social&logoWidth=0)](https://twitter.com/intent/follow?screen_name=fescar)
 
 ## What is FESCAR?
 
@@ -72,7 +71,7 @@ You can view the full documentation from the wiki: [FESCAR wiki page](https://gi
 
 ## Reporting bugs
 
-Please follow the [template](https://github.com/TBD) for reporting any issues.
+Please follow the [template]() for reporting any issues.
 
 
 ## Contributing
@@ -82,7 +81,7 @@ Contributors are welcomed to join the FEATS project. Please check [CONTRIBUTING]
 
 ## Contact
 
-* [Twitter](https://twitter.com/fescar): TBD. Follow along for latest FESCAR news on Twitter.
+* [Twitter](): TBD. Follow along for latest FESCAR news on Twitter.
 * Email Group:
      * dev.fescar@gmail.com: FESCAR developer discussion (APIs, feature design, etc).
 
diff --git a/changeVersion.sh b/changeVersion.sh
new file mode 100644
index 000000000..6a2347f19
--- /dev/null
+++ b/changeVersion.sh
@@ -0,0 +1,5 @@
+echo "./changeVersion.sh oldVersion newVersion"
+echo $1
+echo $2
+find ./ -name pom.xml | grep -v target | xargs perl -pi -e "s|$1|$2|g"
+#find ./ -name Version.java | grep -v target | xargs perl -pi -e "s|$1|$2|g"
\ No newline at end of file
diff --git a/common/pom.xml b/common/pom.xml
index ff9f6b10b..226fccd89 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-common</artifactId>
diff --git a/config/pom.xml b/config/pom.xml
index 4c72a14d3..448116a35 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-config</artifactId>
diff --git a/core/pom.xml b/core/pom.xml
index 3fee8593e..b69f2568d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-core</artifactId>
diff --git a/core/src/main/java/com/alibaba/fescar/core/protocol/Version.java b/core/src/main/java/com/alibaba/fescar/core/protocol/Version.java
index 8ec8c7725..e6d963aa9 100644
--- a/core/src/main/java/com/alibaba/fescar/core/protocol/Version.java
+++ b/core/src/main/java/com/alibaba/fescar/core/protocol/Version.java
@@ -16,15 +16,16 @@
 
 package com.alibaba.fescar.core.protocol;
 
-import com.alibaba.fescar.common.util.NetUtil;
-import io.netty.channel.Channel;
-
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import com.alibaba.fescar.common.util.NetUtil;
+
+import io.netty.channel.Channel;
+
 public class Version {
 
-    public static final String CURRENT = "0.1.0";
+    public static final String CURRENT = "0.1.2";
 
     public static final Map<String, String> VERSION_MAP = new ConcurrentHashMap<String, String>();
 
diff --git a/core/src/main/java/com/alibaba/fescar/core/rpc/ChannelManager.java b/core/src/main/java/com/alibaba/fescar/core/rpc/ChannelManager.java
index 0f9bd5319..f2d41672a 100644
--- a/core/src/main/java/com/alibaba/fescar/core/rpc/ChannelManager.java
+++ b/core/src/main/java/com/alibaba/fescar/core/rpc/ChannelManager.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import com.alibaba.fescar.common.Constants;
+import com.alibaba.fescar.common.exception.FrameworkException;
 import com.alibaba.fescar.core.protocol.*;
 import com.alibaba.fescar.core.protocol.RegisterRMRequest;
 import com.alibaba.fescar.core.protocol.RegisterTMRequest;
@@ -50,21 +51,16 @@ public class ChannelManager {
         = new ConcurrentHashMap<Channel, RpcContext>();
 
     /**
-     * dbkey+appname+ip port context
+     * resourceId -> applicationId -> ip -> port -> RpcContext
      */
-    private static final ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
-        RpcContext>>>>
-        RM_CHANNELS
-        = new ConcurrentHashMap<String, ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
+    private static final ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>>>>
+        RM_CHANNELS = new ConcurrentHashMap<String, ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
         RpcContext>>>>();
 
     /**
      * ip+appname,port
      */
-    private static final ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>> TM_CHANNELS
-        = new ConcurrentHashMap<String, ConcurrentMap<Integer, RpcContext>>();
-
-    private static final ConcurrentMap<String, String> DB_GROUP_MAPPING = new ConcurrentHashMap<String, String>();
+    private static final ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>> TM_CHANNELS = new ConcurrentHashMap<String, ConcurrentMap<Integer, RpcContext>>();
 
     /**
      * Is registered boolean.
@@ -103,12 +99,16 @@ public class ChannelManager {
         return applicationId + Constants.CLIENT_ID_SPLIT_CHAR + getAddressFromChannel(channel);
     }
 
+    private static String[] readClientId(String clientId) {
+        return clientId.split(Constants.CLIENT_ID_SPLIT_CHAR);
+    }
+
     private static RpcContext buildChannelHolder(TransactionRole clientRole, String version, String applicationId,
                                                  String txServiceGroup, String dbkeys, Channel channel) {
         RpcContext holder = new RpcContext();
         holder.setClientRole(clientRole);
         holder.setVersion(version);
-        String clientId = buildClientId(applicationId, channel);
+        holder.setClientId(buildClientId(applicationId, channel));
         holder.setApplicationId(applicationId);
         holder.setTransactionServiceGroup(txServiceGroup);
         holder.addResources(dbKeytoSet(dbkeys));
@@ -314,79 +314,130 @@ public class ChannelManager {
     /**
      * Gets get channel.
      *
-     * @param resourceId    the db key
-     * @param clientIp      the client ip
-     * @param clientAppName the client app name
-     * @return the get channel
+     * @param resourceId Resource ID
+     * @param clientId Client ID - ApplicationId:IP:Port
+     * @return Corresponding channel, NULL if not found.
      */
-    public static Channel getChannel(String resourceId, String clientIp, String clientAppName) {
+    public static Channel getChannel(String resourceId, String clientId) {
         Channel resultChannel = null;
+
+        String[] clientIdInfo = readClientId(clientId);
+
+        if (clientIdInfo == null || clientIdInfo.length != 3) {
+            throw new FrameworkException("Invalid Client ID: " + clientId);
+        }
+
+        String targetApplicationId = clientIdInfo[0];
+        String targetIP = clientIdInfo[1];
+        int targetPort = Integer.parseInt(clientIdInfo[2]);
+
         ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
             RpcContext>>> applicationIdMap = RM_CHANNELS.get(resourceId);
-        if (null != applicationIdMap && !applicationIdMap.isEmpty()) {
-            ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>> clientIpMap = applicationIdMap.get(clientAppName);
-            if (null != clientIpMap && !clientIpMap.isEmpty()) {
-                ConcurrentMap<Integer, RpcContext> portMap = clientIpMap.get(clientIp);
-                if (null != portMap && !portMap.isEmpty()) {
-                    for (ConcurrentMap.Entry<Integer, RpcContext> portMapEntry : portMap.entrySet()) {
-                        Channel channel = portMapEntry.getValue().getChannel();
-                        if (channel.isActive()) {
-                            resultChannel = channel;
-                            break;
+
+        if (targetApplicationId == null || applicationIdMap.isEmpty()) {
+            if (LOGGER.isInfoEnabled()) {
+                LOGGER.info("No channel is available for resource[" + resourceId + "]");
+            }
+            return null;
+        }
+
+        ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>> ipMap = applicationIdMap.get(targetApplicationId);
+
+        if (null != ipMap && !ipMap.isEmpty()) {
+
+            // Firstly, try to find the original channel through which the branch was registered.
+            ConcurrentMap<Integer, RpcContext> portMapOnTargetIP = ipMap.get(targetIP);
+            if (portMapOnTargetIP != null && !portMapOnTargetIP.isEmpty()) {
+
+                RpcContext exactRpcContext = portMapOnTargetIP.get(targetPort);
+                if (exactRpcContext != null) {
+                    Channel channel = exactRpcContext.getChannel();
+                    if (channel.isActive()) {
+                        resultChannel = channel;
+                        if (LOGGER.isInfoEnabled()) {
+                            LOGGER.info("Just got exactly the one " + channel + " for " + clientId);
+                        }
+                    } else {
+                        if (portMapOnTargetIP.remove(targetPort, exactRpcContext)) {
+                            if (LOGGER.isInfoEnabled()) {
+                                LOGGER.info("Removed inactive " + channel);
+                            }
                         }
-                        portMap.remove(portMapEntry.getKey());
                     }
                 }
-                if (null == resultChannel) {
-                    for (ConcurrentMap.Entry<String, ConcurrentMap<Integer, RpcContext>> clientIpMapEntry : clientIpMap
-                        .entrySet()) {
-                        if (clientIpMapEntry.getKey().equals(clientIp)) { continue; }
-                        for (ConcurrentMap.Entry<Integer, RpcContext> portMapEntry : clientIpMapEntry.getValue()
-                            .entrySet()) {
-                            Channel channel = portMapEntry.getValue().getChannel();
-                            if (channel.isActive()) {
-                                resultChannel = channel;
+
+                // The original channel was broken, try another one.
+                if (resultChannel == null) {
+                    for (ConcurrentMap.Entry<Integer, RpcContext> portMapOnTargetIPEntry : portMapOnTargetIP.entrySet()) {
+                        Channel channel = portMapOnTargetIPEntry.getValue().getChannel();
+
+                        if (channel.isActive()) {
+                            resultChannel = channel;
+                            if (LOGGER.isInfoEnabled()) {
+                                LOGGER.info("Choose " + channel + " on the same IP[" + targetIP + "]  as alternative of "
+                                        + clientId);
+                            }
+                            break;
+                        } else {
+                            if (portMapOnTargetIP.remove(portMapOnTargetIPEntry.getKey(), portMapOnTargetIPEntry.getValue())) {
                                 if (LOGGER.isInfoEnabled()) {
-                                    LOGGER.info(
-                                        "use same appname:" + clientAppName + ",channel:" + channel + ",replace ip:"
-                                            + clientIp);
+                                    LOGGER.info("Removed inactive " + channel);
                                 }
-                                break;
                             }
-                            clientIpMapEntry.getValue().remove(portMapEntry.getKey());
-                        }
-                        if (null != resultChannel) {
-                            break;
                         }
                     }
                 }
             }
-        }
-        if (null == resultChannel) {
-            for (ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
-                RpcContext>>> appIdEntryMap : RM_CHANNELS.values()) {
-                for (ConcurrentMap<String, ConcurrentMap<Integer,
-                    RpcContext>> clientIpEntryMap : appIdEntryMap.values()) {
-                    if (clientIpEntryMap.containsKey(clientIp)) {
-                        for (ConcurrentMap.Entry<Integer, RpcContext> portMapEntry : clientIpEntryMap.get(clientIp)
-                            .entrySet()) {
-                            Channel channel = portMapEntry.getValue().getChannel();
-                            if (channel.isActive()) {
-                                resultChannel = channel;
+
+            // No channel on the this app node, try another one.
+            if (resultChannel == null) {
+                for (ConcurrentMap.Entry<String, ConcurrentMap<Integer, RpcContext>> ipMapEntry : ipMap
+                    .entrySet()) {
+                    if (ipMapEntry.getKey().equals(targetIP)) { continue; }
+
+                    ConcurrentMap<Integer, RpcContext> portMapOnOtherIP = ipMapEntry.getValue();
+                    if (portMapOnOtherIP == null || portMapOnOtherIP.isEmpty()) {
+                        continue;
+                    }
+
+                    for (ConcurrentMap.Entry<Integer, RpcContext> portMapOnOtherIPEntry : portMapOnOtherIP.entrySet()) {
+                        Channel channel = portMapOnOtherIPEntry.getValue().getChannel();
+
+                        if (channel.isActive()) {
+                            resultChannel = channel;
+                            if (LOGGER.isInfoEnabled()) {
+                                LOGGER.info("Choose " + channel + " on the same application[" + targetApplicationId
+                                    + "] as alternative of "
+                                    + clientId);
+                            }
+                            break;
+                        } else {
+                            if (portMapOnOtherIP.remove(portMapOnOtherIPEntry.getKey(), portMapOnOtherIPEntry.getValue())) {
                                 if (LOGGER.isInfoEnabled()) {
-                                    LOGGER.info("use another dbkey,channel:" + channel);
+                                    LOGGER.info("Removed inactive " + channel);
                                 }
-                                break;
                             }
-                            clientIpEntryMap.get(clientIp).remove(portMapEntry.getKey());
                         }
                     }
-                    if (null != resultChannel) {
-                        break;
-                    }
+                    if (resultChannel != null) { break; }
+                }
+            }
+        }
+
+        if (resultChannel == null) {
+            resultChannel = tryOtherApp(applicationIdMap, targetApplicationId);
+
+            if (resultChannel == null) {
+                if (LOGGER.isInfoEnabled()) {
+                    LOGGER.info("No channel is available for resource[" + resourceId
+                        + "]  as alternative of "
+                        + clientId);
                 }
-                if (null != resultChannel) {
-                    break;
+            } else {
+                if (LOGGER.isInfoEnabled()) {
+                    LOGGER.info("Choose " + resultChannel + " on the same resource[" + resourceId
+                        + "]  as alternative of "
+                        + clientId);
                 }
             }
         }
@@ -394,4 +445,44 @@ public class ChannelManager {
         return resultChannel;
 
     }
+
+    private static Channel tryOtherApp(ConcurrentMap<String, ConcurrentMap<String, ConcurrentMap<Integer,
+        RpcContext>>> applicationIdMap, String myApplicationId) {
+        Channel chosenChannel = null;
+        for (ConcurrentMap.Entry<String, ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>>> applicationIdMapEntry : applicationIdMap.entrySet()) {
+            if (applicationIdMapEntry.getKey().equals(myApplicationId)) {
+                continue;
+            }
+
+            ConcurrentMap<String, ConcurrentMap<Integer, RpcContext>> targetIPMap = applicationIdMapEntry.getValue();
+            if (targetIPMap == null || targetIPMap.isEmpty()) {
+                continue;
+            }
+
+            for (ConcurrentMap.Entry<String, ConcurrentMap<Integer, RpcContext>> targetIPMapEntry : targetIPMap.entrySet()) {
+                ConcurrentMap<Integer, RpcContext> portMap = targetIPMapEntry.getValue();
+                if (portMap == null || portMap.isEmpty()) {
+                    continue;
+                }
+
+                for (ConcurrentMap.Entry<Integer, RpcContext> portMapEntry : portMap.entrySet()) {
+                    Channel channel = portMapEntry.getValue().getChannel();
+                    if (channel.isActive()) {
+                        chosenChannel = channel;
+                        break;
+                    } else {
+                        if (portMap.remove(portMapEntry.getKey(), portMapEntry.getValue())) {
+                            if (LOGGER.isInfoEnabled()) {
+                                LOGGER.info("Removed inactive " + channel);
+                            }
+                        }
+                    }
+                }
+                if (chosenChannel != null) { break; }
+            }
+            if (chosenChannel != null) { break; }
+        }
+        return chosenChannel;
+
+    }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/rpc/RpcContext.java b/core/src/main/java/com/alibaba/fescar/core/rpc/RpcContext.java
index 3fb65400f..caeca6010 100644
--- a/core/src/main/java/com/alibaba/fescar/core/rpc/RpcContext.java
+++ b/core/src/main/java/com/alibaba/fescar/core/rpc/RpcContext.java
@@ -51,6 +51,8 @@ public class RpcContext {
 
     private String transactionServiceGroup;
 
+    private String clientId;
+
     private Channel channel;
 
     private Set<String> resourceSets;
@@ -175,7 +177,7 @@ public class RpcContext {
      * @return the get client id
      */
     public String getClientId() {
-        return getClientIp();
+        return clientId;
     }
 
     /**
@@ -277,20 +279,6 @@ public class RpcContext {
         return address;
     }
 
-    /**
-     * Gets get client ip.
-     *
-     * @return the get client ip
-     */
-    public String getClientIp() {
-        String address = getAddressFromChannel(channel);
-        String clientIp = address;
-        if (clientIp.contains(Constants.IP_PORT_SPLIT_CHAR)) {
-            clientIp = clientIp.substring(0, clientIp.lastIndexOf(Constants.IP_PORT_SPLIT_CHAR));
-        }
-        return clientIp;
-    }
-
     private static Integer getClientPortFromChannel(Channel channel) {
         String address = getAddressFromChannel(channel);
         Integer port = 0;
@@ -347,19 +335,18 @@ public class RpcContext {
         this.resourceSets.addAll(resource);
     }
 
-    /**
-     * To string string.
-     *
-     * @return the string
-     */
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("vgroup:" + transactionServiceGroup);
-        sb.append(",appname:" + applicationId);
-        sb.append(",channel:" + channel);
-        sb.append(",version:" + version);
-        sb.append(",type:" + clientRole.name());
-        return sb.toString();
+        return "RpcContext{" +
+            "applicationId='" + applicationId + '\'' +
+            ", transactionServiceGroup='" + transactionServiceGroup + '\'' +
+            ", clientId='" + clientId + '\'' +
+            ", channel=" + channel +
+            ", resourceSets=" + resourceSets +
+            '}';
     }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/rpc/ServerMessageSender.java b/core/src/main/java/com/alibaba/fescar/core/rpc/ServerMessageSender.java
index b17620b86..c5b59cb1c 100644
--- a/core/src/main/java/com/alibaba/fescar/core/rpc/ServerMessageSender.java
+++ b/core/src/main/java/com/alibaba/fescar/core/rpc/ServerMessageSender.java
@@ -29,26 +29,6 @@ import io.netty.channel.Channel;
  * @Description:
  */
 public interface ServerMessageSender {
-    /**
-     * Send request to client by the upper level.
-     * Actually, just BranchCommitRequest/BranchRollbackRequest will be sent from server to RM client.
-     * No case for sending a request to TM client. ResourceId is useless by case of sending a TM request.
-     *
-     * @param resourceId resourceId
-     * @param clientIP IP of the target client
-     * @param applicationId applicationId of the target client
-     * @param request request to be sent
-     */
-    void sendRequest(String resourceId, String clientIP, String applicationId, Object request /* TODO: this should be AbstractMessage */);
-
-    /**
-     * @param msgId
-     * @param dbKey
-     * @param clientIp
-     * @param clientAppName
-     * @param msg
-     */
-    void sendResponse(long msgId, String dbKey, String clientIp, String clientAppName, Object msg);
 
     /**
      * @param msgId
@@ -58,28 +38,26 @@ public interface ServerMessageSender {
     void sendResponse(long msgId, Channel channel, Object msg);
 
     /**
-     * 同步调用client
+     * Sync call to RM with timeout.
      *
-     * @param dbKey
-     * @param clientIp
-     * @param clientAppName
-     * @param msg
-     * @return
-     * @throws IOException
+     * @param resourceId Resource ID
+     * @param clientId Client ID
+     * @param message Request message
+     * @param timeout timeout of the call
+     * @return Response message
+     * @throws IOException .
      */
-    Object sendSynRequest(String dbKey, String clientIp, String clientAppName,
-                          Object msg, long timeout) throws IOException, TimeoutException;
+    Object sendSyncRequest(String resourceId, String clientId, Object message, long timeout) throws IOException, TimeoutException;
 
     /**
-     * 同步调用client
+     * Sync call to RM
      *
-     * @param dbKey
-     * @param clientIp
-     * @param clientAppName
-     * @param msg
-     * @return
-     * @throws IOException
+     *
+     * @param resourceId Resource ID
+     * @param clientId Client ID
+     * @param message Request message
+     * @return Response message
+     * @throws IOException .
      */
-    Object sendSynRequest(String dbKey, String clientIp, String clientAppName,
-                          Object msg) throws IOException, TimeoutException;
+    Object sendSyncRequest(String resourceId, String clientId, Object message) throws IOException, TimeoutException;
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/rpc/netty/RpcServer.java b/core/src/main/java/com/alibaba/fescar/core/rpc/netty/RpcServer.java
index ad94d6a2f..73c212bb5 100644
--- a/core/src/main/java/com/alibaba/fescar/core/rpc/netty/RpcServer.java
+++ b/core/src/main/java/com/alibaba/fescar/core/rpc/netty/RpcServer.java
@@ -19,8 +19,6 @@ package com.alibaba.fescar.core.rpc.netty;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeoutException;
 
-import com.alibaba.fescar.common.exception.FrameworkErrorCode;
-import com.alibaba.fescar.common.exception.FrameworkException;
 import com.alibaba.fescar.common.util.NetUtil;
 import com.alibaba.fescar.core.protocol.HeartbeatMessage;
 import com.alibaba.fescar.core.protocol.RegisterRMRequest;
@@ -182,62 +180,6 @@ public class RpcServer extends AbstractRpcRemotingServer implements ServerMessag
         }
     }
 
-    /**
-     * Send request.
-     * handle async branch commit and rollback
-     *
-     * @param dbKey         the db key
-     * @param clientIp      the client ip
-     * @param clientAppName the client app name
-     * @param msg           the msg
-     */
-    @Override
-    public void sendRequest(String dbKey, String clientIp, String clientAppName, Object msg) {
-        Channel clientChannel = ChannelManager.getChannel(dbKey, clientIp, clientAppName);
-        if (clientChannel == null) {
-            throw new FrameworkException("rm client is not connected. dbkey:" + dbKey
-                + ",clientIp:" + clientIp);
-        } else {
-            try {
-                super.sendRequest(clientChannel, msg);
-            } catch (FrameworkException e) {
-                if (e.getErrcode() == FrameworkErrorCode.ChannelIsNotWritable) {
-                    ChannelManager.releaseRpcContext(clientChannel);
-                    throw e;
-                }
-            }
-        }
-    }
-
-    /**
-     * Send response.
-     * redress,merge msg
-     *
-     * @param msgId         the msg id
-     * @param dbKey         the db key
-     * @param clientIp      the client ip
-     * @param clientAppName the client app name
-     * @param msg           the msg
-     */
-    @Override
-    public void sendResponse(long msgId, String dbKey, String clientIp, String clientAppName,
-                             Object msg) {
-        Channel clientChannel = ChannelManager.getChannel(dbKey, clientIp, clientAppName);
-        if (clientChannel != null) {
-            try {
-                super.sendResponse(msgId, clientChannel, msg);
-            } catch (FrameworkException e) {
-                if (e.getErrcode() == FrameworkErrorCode.ChannelIsNotWritable) {
-                    LOGGER.error("channel is not writeable,channel:" + clientChannel);
-                    ChannelManager.releaseRpcContext(clientChannel);
-                }
-                throw e;
-            }
-        } else {
-            throw new RuntimeException("channel is error. channel:" + clientChannel);
-        }
-    }
-
     /**
      * Send response.
      * rm reg,rpc reg,inner response
@@ -263,40 +205,38 @@ public class RpcServer extends AbstractRpcRemotingServer implements ServerMessag
      * Send request with response object.
      * send syn request for rm
      *
-     * @param dbKey         the db key
-     * @param clientIp      the client ip
-     * @param clientAppName the client app name
-     * @param msg           the msg
+     * @param resourceId         the db key
+     * @param clientId      the client ip
+     * @param message           the message
      * @param timeout       the timeout
      * @return the object
      * @throws TimeoutException the timeout exception
      */
     @Override
-    public Object sendSynRequest(String dbKey, String clientIp, String clientAppName, Object msg,
-                                 long timeout) throws TimeoutException {
-        Channel clientChannel = ChannelManager.getChannel(dbKey, clientIp, clientAppName);
-        if (clientChannel != null) {
-            return super.sendAsyncRequestWithResponse(null, clientChannel, msg, timeout);
-        } else {
-            throw new RuntimeException("rm client is not connected. dbkey:" + dbKey
-                + ",clientIp:" + clientIp);
+    public Object sendSyncRequest(String resourceId, String clientId, Object message,
+                                  long timeout) throws TimeoutException {
+        Channel clientChannel = ChannelManager.getChannel(resourceId, clientId);
+        if (clientChannel == null) {
+            throw new RuntimeException("rm client is not connected. dbkey:" + resourceId
+                + ",clientId:" + clientId);
+
         }
+        return sendAsyncRequestWithResponse(null, clientChannel, message, timeout);
     }
 
     /**
      * Send request with response object.
      *
-     * @param dbKey         the db key
-     * @param clientIp      the client ip
-     * @param clientAppName the client app name
-     * @param msg           the msg
+     * @param resourceId         the db key
+     * @param clientId      the client ip
+     * @param message           the msg
      * @return the object
      * @throws TimeoutException the timeout exception
      */
     @Override
-    public Object sendSynRequest(String dbKey, String clientIp, String clientAppName, Object msg)
+    public Object sendSyncRequest(String resourceId, String clientId, Object message)
         throws TimeoutException {
-        return sendSynRequest(dbKey, clientIp, clientAppName, msg, NettyServerConfig.getRpcRequestTimeout());
+        return sendSyncRequest(resourceId, clientId, message, NettyServerConfig.getRpcRequestTimeout());
     }
 
     /**
diff --git a/distribution/pom.xml b/distribution/pom.xml
index a30f541a4..be83dd713 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-distribution</artifactId>
diff --git a/dubbo/pom.xml b/dubbo/pom.xml
index 0ac14dcdd..aef91b54a 100644
--- a/dubbo/pom.xml
+++ b/dubbo/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-dubbo</artifactId>
diff --git a/examples/pom.xml b/examples/pom.xml
index 6cec39167..563679267 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-examples</artifactId>
diff --git a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/AccountServiceImpl.java b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/AccountServiceImpl.java
index 401fa22f3..32db941b0 100644
--- a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/AccountServiceImpl.java
+++ b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/AccountServiceImpl.java
@@ -46,9 +46,10 @@ public class AccountServiceImpl implements AccountService {
     @Override
     public void debit(String userId, int money) {
         LOGGER.info("Account Service ... xid: " + RootContext.getXID());
+        LOGGER.info("Deducting balance SQL: update account_tbl set money = money - {} where user_id = {}",money,userId);
+
         jdbcTemplate.update("update account_tbl set money = money - ? where user_id = ?", new Object[] {money, userId});
         LOGGER.info("Account Service End ... ");
-
     }
 
     public static void main(String[] args) throws Throwable {
@@ -56,6 +57,8 @@ public class AccountServiceImpl implements AccountService {
         String applicationId = "dubbo-demo-account-service";
         String txServiceGroup = "my_test_tx_group";
 
+        RMClientAT.init(applicationId, txServiceGroup);
+
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-account-service.xml"});
         context.getBean("service");
         JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
diff --git a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/BusinessServiceImpl.java b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/BusinessServiceImpl.java
index d6341d9f5..c86a245f7 100644
--- a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/BusinessServiceImpl.java
+++ b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/BusinessServiceImpl.java
@@ -44,6 +44,7 @@ public class BusinessServiceImpl implements BusinessService {
     public void purchase(String userId, String commodityCode, int orderCount) {
         storageService.deduct(commodityCode, orderCount);
         orderService.create(userId, commodityCode, orderCount);
+        throw new RuntimeException("xxx");
 
     }
 
diff --git a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/OrderServiceImpl.java b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/OrderServiceImpl.java
index 1ed436b56..5bed819cb 100644
--- a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/OrderServiceImpl.java
+++ b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/OrderServiceImpl.java
@@ -21,6 +21,7 @@ import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
 import com.alibaba.fescar.core.context.RootContext;
+import com.alibaba.fescar.rm.RMClientAT;
 import com.alibaba.fescar.test.common.ApplicationKeeper;
 import com.alibaba.fescar.tm.dubbo.AccountService;
 import com.alibaba.fescar.tm.dubbo.Order;
@@ -66,6 +67,8 @@ public class OrderServiceImpl implements OrderService {
 
         KeyHolder keyHolder = new GeneratedKeyHolder();
 
+        LOGGER.info("Order Service SQL: insert into order_tbl (user_id, commodity_code, count, money) values ({}, {}, {}, {})" ,userId ,commodityCode ,orderCount ,orderMoney );
+
         jdbcTemplate.update(new PreparedStatementCreator() {
 
             @Override
@@ -81,7 +84,7 @@ public class OrderServiceImpl implements OrderService {
             }
         }, keyHolder);
 
-        order.id = keyHolder.getKey().longValue();
+        order.id = (long)keyHolder.getKey();
 
         LOGGER.info("Order Service End ... Created " + order);
 
@@ -105,6 +108,8 @@ public class OrderServiceImpl implements OrderService {
         String applicationId = "dubbo-demo-order-service";
         String txServiceGroup = "my_test_tx_group";
 
+        RMClientAT.init(applicationId, txServiceGroup);
+
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-order-service.xml"});
         context.getBean("service");
         JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
diff --git a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/StorageServiceImpl.java b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/StorageServiceImpl.java
index c6a00357f..8f1839740 100644
--- a/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/StorageServiceImpl.java
+++ b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/StorageServiceImpl.java
@@ -45,6 +45,8 @@ public class StorageServiceImpl implements StorageService {
     @Override
     public void deduct(String commodityCode, int count) {
         LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID());
+        LOGGER.info("Deducting inventory SQL: update storage_tbl set count = count - {} where commodity_code = {}",count,commodityCode);
+
         jdbcTemplate.update("update storage_tbl set count = count - ? where commodity_code = ?", new Object[] {count, commodityCode});
         LOGGER.info("Storage Service End ... ");
 
@@ -55,6 +57,8 @@ public class StorageServiceImpl implements StorageService {
         String applicationId = "dubbo-demo-storage-service";
         String txServiceGroup = "my_test_tx_group";
 
+        RMClientAT.init(applicationId, txServiceGroup);
+
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-storage-service.xml"});
         context.getBean("service");
         JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
diff --git a/pom.xml b/pom.xml
index 3cf225acf..24a401967 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
 
     <groupId>com.alibaba.fescar</groupId>
     <artifactId>fescar-all</artifactId>
-    <version>0.1.0-SNAPSHOT</version>
+    <version>0.1.2-SNAPSHOT</version>
     <modules>
         <module>config</module>
         <module>common</module>
diff --git a/rm-datasource/pom.xml b/rm-datasource/pom.xml
index fa048cf1c..6d5e84ebf 100644
--- a/rm-datasource/pom.xml
+++ b/rm-datasource/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-rm-datasource</artifactId>
diff --git a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/undo/UndoLogManager.java b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/undo/UndoLogManager.java
index ee31ec904..b170ec136 100644
--- a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/undo/UndoLogManager.java
+++ b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/undo/UndoLogManager.java
@@ -50,7 +50,7 @@ public final class UndoLogManager {
     private static String DELETE_UNDO_LOG_SQL = "DELETE FROM " + UNDO_LOG_TABLE_NAME + "\n" +
             "\tWHERE branch_id = ? AND xid = ?";
 
-    private static String SELECT_UNDO_LOG_SQL = "SELECT * FROM undo_log WHERE log_status = 0 AND branch_id = ? AND xid = ? FOR UPDATE";
+    private static String SELECT_UNDO_LOG_SQL = "SELECT * FROM " + UNDO_LOG_TABLE_NAME + " WHERE log_status = 0 AND branch_id = ? AND xid = ? FOR UPDATE";
 
     private UndoLogManager() {
 
diff --git a/server/pom.xml b/server/pom.xml
index 31001faa5..f74e7bdd4 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-server</artifactId>
diff --git a/server/src/main/java/com/alibaba/fescar/server/coordinator/DefaultCoordinator.java b/server/src/main/java/com/alibaba/fescar/server/coordinator/DefaultCoordinator.java
index 5e6c7011d..dcf8f3ca5 100644
--- a/server/src/main/java/com/alibaba/fescar/server/coordinator/DefaultCoordinator.java
+++ b/server/src/main/java/com/alibaba/fescar/server/coordinator/DefaultCoordinator.java
@@ -144,8 +144,8 @@ public class DefaultCoordinator extends AbstractTCInboundHandler
             GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid));
             BranchSession branchSession = globalSession.getBranch(branchId);
 
-            BranchCommitResponse response = (BranchCommitResponse)messageSender.sendSynRequest(resourceId,
-                branchSession.getClientId(), globalSession.getApplicationId(), request);
+            BranchCommitResponse response = (BranchCommitResponse)messageSender.sendSyncRequest(resourceId,
+                branchSession.getClientId(), request);
             return response.getBranchStatus();
         } catch (IOException e) {
             throw new TransactionException(FailedToSendBranchCommitRequest, branchId + "/" + xid, e);
@@ -168,8 +168,8 @@ public class DefaultCoordinator extends AbstractTCInboundHandler
             GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid));
             BranchSession branchSession = globalSession.getBranch(branchId);
 
-            BranchRollbackResponse response = (BranchRollbackResponse)messageSender.sendSynRequest(resourceId,
-                branchSession.getClientId(), globalSession.getApplicationId(), request);
+            BranchRollbackResponse response = (BranchRollbackResponse)messageSender.sendSyncRequest(resourceId,
+                branchSession.getClientId(), request);
             return response.getBranchStatus();
         } catch (IOException e) {
             throw new TransactionException(FailedToSendBranchRollbackRequest, branchId + "/" + xid, e);
diff --git a/spring/pom.xml b/spring/pom.xml
index 075333225..7140c055b 100644
--- a/spring/pom.xml
+++ b/spring/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-spring</artifactId>
diff --git a/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionScanner.java b/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionScanner.java
index cc175c0ef..3b0451b69 100644
--- a/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionScanner.java
+++ b/spring/src/main/java/com/alibaba/fescar/spring/annotation/GlobalTransactionScanner.java
@@ -180,10 +180,9 @@ public class GlobalTransactionScanner extends AbstractAutoProxyCreator implement
                 if (PROXYED_SET.contains(beanName)) {
                     return bean;
                 }
-                PROXYED_SET.add(beanName);
                 Class<?> serviceInterface = findTargetClass(bean);
                 Method[] methods = serviceInterface.getMethods();
-                LinkedList<MethodDesc> methodDescList = new LinkedList<MethodDesc>();
+                LinkedList<MethodDesc> methodDescList = new LinkedList<>();
                 for (Method method : methods) {
                     GlobalTransactional anno = method.getAnnotation(GlobalTransactional.class);
                     if (anno != null) {
@@ -193,8 +192,9 @@ public class GlobalTransactionScanner extends AbstractAutoProxyCreator implement
                 if (methodDescList.isEmpty()) {
                     return bean;
                 }
-
-                interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
+                if (interceptor == null) {
+                    interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
+                }
                 if (!AopUtils.isAopProxy(bean)) {
                     bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                 } else {
@@ -204,6 +204,7 @@ public class GlobalTransactionScanner extends AbstractAutoProxyCreator implement
                         advised.addAdvisor(0, avr);
                     }
                 }
+                PROXYED_SET.add(beanName);
                 return bean;
             }
         } catch (Exception exx) {
diff --git a/tm/pom.xml b/tm/pom.xml
index e0ae87422..2c30b5325 100644
--- a/tm/pom.xml
+++ b/tm/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-tm</artifactId>


Test patch:
diff --git a/test/pom.xml b/test/pom.xml
index 1163f5bd5..674d60f5c 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>fescar-all</artifactId>
         <groupId>com.alibaba.fescar</groupId>
-        <version>0.1.0-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>fescar-test</artifactId>