Repository: alibaba/fescar
Pull request #273 (2019-01-24 15:27:25)

ths isSending flag should be set as volatile type for multi-thread read and write without synchronized
ths isSending flag should be set as volatile type for multi-thread read and write without synchronized

codecov-io: # [Codecov](https://codecov.io/gh/alibaba/fescar/pull/273?src=pr&el=h1) Report
> Merging [#273](https://codecov.io/gh/alibaba/fescar/pull/273?src=pr&el=desc) into [develop](https://codecov.io/gh/alibaba/fescar/commit/ceb1fc5383df68eb4d11a1d207f54956a14df7a8?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `0%`.

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

```diff
@@           Coverage Diff            @@
##           develop     #273   +/-   ##
========================================
  Coverage    12.15%   12.15%           
========================================
  Files          184      184           
  Lines         6834     6834           
  Branches       937      937           
========================================
  Hits           831      831           
  Misses        5924     5924           
  Partials        79       79
```


| [Impacted Files](https://codecov.io/gh/alibaba/fescar/pull/273?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...aba/fescar/core/rpc/netty/AbstractRpcRemoting.java](https://codecov.io/gh/alibaba/fescar/pull/273/diff?src=pr&el=tree#diff-Y29yZS9zcmMvbWFpbi9qYXZhL2NvbS9hbGliYWJhL2Zlc2Nhci9jb3JlL3JwYy9uZXR0eS9BYnN0cmFjdFJwY1JlbW90aW5nLmphdmE=) | `0% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/alibaba/fescar/pull/273?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/273?src=pr&el=footer). Last update [ceb1fc5...dc0544a](https://codecov.io/gh/alibaba/fescar/pull/273?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/config/pom.xml b/config/pom.xml
index 448116a35..13ca41633 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -51,6 +51,12 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 </project>
diff --git a/config/src/main/java/com/alibaba/fescar/config/ConfigChangeListener.java b/config/src/main/java/com/alibaba/fescar/config/ConfigChangeListener.java
index a491f7e6e..a7073ecd2 100644
--- a/config/src/main/java/com/alibaba/fescar/config/ConfigChangeListener.java
+++ b/config/src/main/java/com/alibaba/fescar/config/ConfigChangeListener.java
@@ -16,7 +16,7 @@
 
 package com.alibaba.fescar.config;
 
-import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 
 /**
  * The interface Config change listener.
@@ -34,7 +34,7 @@ public interface ConfigChangeListener {
      *
      * @return the executor
      */
-    Executor getExecutor();
+    ExecutorService getExecutor();
 
     /**
      * Receive config info.
diff --git a/config/src/main/java/com/alibaba/fescar/config/Configuration.java b/config/src/main/java/com/alibaba/fescar/config/Configuration.java
index 76cabd738..1d4ae232e 100644
--- a/config/src/main/java/com/alibaba/fescar/config/Configuration.java
+++ b/config/src/main/java/com/alibaba/fescar/config/Configuration.java
@@ -16,11 +16,13 @@
 
 package com.alibaba.fescar.config;
 
+import java.util.List;
+
 /**
  * The interface Configuration.
  *
  * @Author: jimin.jm @alibaba-inc.com
- * @Project: fescar-all
+ * @Project: fescar -all
  * @DateTime: 2018 /12/20 14:22
  * @FileName: Configuration
  * @Description:
@@ -215,4 +217,12 @@ public interface Configuration {
      */
     void removeConfigListener(String dataId, ConfigChangeListener listener);
 
+    /**
+     * Gets config listeners.
+     *
+     * @param dataId the data id
+     * @return the config listeners
+     */
+    List<ConfigChangeListener> getConfigListeners(String dataId);
+
 }
diff --git a/config/src/main/java/com/alibaba/fescar/config/FileConfiguration.java b/config/src/main/java/com/alibaba/fescar/config/FileConfiguration.java
index 0e30681b9..294013e19 100644
--- a/config/src/main/java/com/alibaba/fescar/config/FileConfiguration.java
+++ b/config/src/main/java/com/alibaba/fescar/config/FileConfiguration.java
@@ -16,24 +16,23 @@
 
 package com.alibaba.fescar.config;
 
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
 import com.alibaba.fescar.common.thread.NamedThreadFactory;
 import com.alibaba.fescar.config.ConfigFuture.ConfigOperation;
-
 import com.typesafe.config.Config;
 import com.typesafe.config.ConfigFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.*;
+
 /**
  * The type FileConfiguration.
  *
  * @Author: jimin.jm @alibaba-inc.com
- * @Project: fescar-all
+ * @Project: fescar -all
  * @DateTime: 2018 /9/10 11:34
  * @FileName: FileConfiguration
  * @Description:
@@ -46,19 +45,33 @@ public class FileConfiguration implements Configuration {
 
     private ExecutorService configOperateExecutor;
 
+    private ExecutorService configChangeExecutor;
+
     private static final int CORE_CONFIG_OPERATE_THREAD = 1;
 
+    private static final int CORE_CONFIG_CHANGE_THREAD = 1;
+
     private static final int MAX_CONFIG_OPERATE_THREAD = 2;
 
     private static final long DEFAULT_CONFIG_TIMEOUT = 5 * 1000;
 
+    private static final long LISTENER_CONFIG_INTERNAL = 1 * 1000;
+
+    private final ConcurrentMap<String, List<ConfigChangeListener>> configListenersMap = new ConcurrentHashMap<>();
+
+    private final ConcurrentMap<String, String> listenedConfigMap = new ConcurrentHashMap<>();
+
     /**
      * Instantiates a new File configuration.
      */
     public FileConfiguration() {
         configOperateExecutor = new ThreadPoolExecutor(CORE_CONFIG_OPERATE_THREAD, MAX_CONFIG_OPERATE_THREAD,
             Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
-            new NamedThreadFactory("configOperate", MAX_CONFIG_OPERATE_THREAD, true));
+            new NamedThreadFactory("configOperate", MAX_CONFIG_OPERATE_THREAD));
+        configChangeExecutor = new ThreadPoolExecutor(CORE_CONFIG_CHANGE_THREAD, CORE_CONFIG_CHANGE_THREAD,
+            Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
+            new NamedThreadFactory("configChange", CORE_CONFIG_CHANGE_THREAD));
+        configChangeExecutor.submit(new ConfigChangeRunnable());
     }
 
     @Override
@@ -113,7 +126,7 @@ public class FileConfiguration implements Configuration {
     public String getConfig(String dataId, String defaultValue, long timeoutMills) {
         ConfigFuture configFuture = new ConfigFuture(dataId, defaultValue, ConfigOperation.GET, timeoutMills);
         configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
-        return (String)configFuture.get();
+        return (String)configFuture.get(timeoutMills, TimeUnit.MILLISECONDS);
     }
 
     @Override
@@ -147,7 +160,7 @@ public class FileConfiguration implements Configuration {
     public boolean putConfigIfAbsent(String dataId, String content, long timeoutMills) {
         ConfigFuture configFuture = new ConfigFuture(dataId, content, ConfigOperation.PUTIFABSENT, timeoutMills);
         configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
-        return (Boolean)configFuture.get();
+        return (Boolean)configFuture.get(timeoutMills, TimeUnit.MILLISECONDS);
     }
 
     @Override
@@ -159,7 +172,7 @@ public class FileConfiguration implements Configuration {
     public boolean removeConfig(String dataId, long timeoutMills) {
         ConfigFuture configFuture = new ConfigFuture(dataId, null, ConfigOperation.REMOVE, timeoutMills);
         configOperateExecutor.submit(new ConfigOperateRunnable(configFuture));
-        return (Boolean)configFuture.get();
+        return (Boolean)configFuture.get(timeoutMills, TimeUnit.MILLISECONDS);
     }
 
     @Override
@@ -169,12 +182,36 @@ public class FileConfiguration implements Configuration {
 
     @Override
     public void addConfigListener(String dataId, ConfigChangeListener listener) {
-
+        configListenersMap.putIfAbsent(dataId, new ArrayList<ConfigChangeListener>());
+        configListenersMap.get(dataId).add(listener);
+        if (null != listener.getExecutor()) {
+            ConfigChangeRunnable configChangeTask = new ConfigChangeRunnable(dataId, listener);
+            listener.getExecutor().submit(configChangeTask);
+        }
     }
 
     @Override
     public void removeConfigListener(String dataId, ConfigChangeListener listener) {
+        List<ConfigChangeListener> configChangeListeners = getConfigListeners(dataId);
+        if (configChangeListeners == null) {
+            return;
+        }
+        List<ConfigChangeListener> newChangeListenerList = new ArrayList<>();
+        for (ConfigChangeListener changeListener : configChangeListeners) {
+            if (!changeListener.equals(listener)) {
+                newChangeListenerList.add(changeListener);
+            }
+        }
+        configListenersMap.put(dataId, newChangeListenerList);
+        if (null != listener.getExecutor()) {
+            listener.getExecutor().shutdownNow();
+        }
+
+    }
 
+    @Override
+    public List<ConfigChangeListener> getConfigListeners(String dataId) {
+        return configListenersMap.get(dataId);
     }
 
     /**
@@ -198,6 +235,7 @@ public class FileConfiguration implements Configuration {
             if (null != configFuture) {
                 if (configFuture.isTimeout()) {
                     setFailResult(configFuture);
+                    return;
                 }
                 if (configFuture.getOperation() == ConfigOperation.GET) {
                     String result = CONFIG.getString(configFuture.getDataId());
@@ -226,4 +264,81 @@ public class FileConfiguration implements Configuration {
 
     }
 
+    /**
+     * The type Config change runnable.
+     */
+    class ConfigChangeRunnable implements Runnable {
+
+        private String dataId;
+        private ConfigChangeListener listener;
+
+        /**
+         * Instantiates a new Config change runnable.
+         */
+        public ConfigChangeRunnable() {}
+
+        /**
+         * Instantiates a new Config change runnable.
+         *
+         * @param dataId   the data id
+         * @param listener the listener
+         */
+        public ConfigChangeRunnable(String dataId, ConfigChangeListener listener) {
+
+            if (null == listener.getExecutor()) {
+                throw new IllegalArgumentException("getExecutor is null.");
+            }
+            this.dataId = dataId;
+            this.listener = listener;
+        }
+
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    Map<String, List<ConfigChangeListener>> configListenerMap;
+                    if (null != dataId && null != listener) {
+                        configListenerMap = new ConcurrentHashMap<>();
+                        configListenerMap.put(dataId, new ArrayList<ConfigChangeListener>());
+                        configListenerMap.get(dataId).add(listener);
+                    } else {
+                        configListenerMap = configListenersMap;
+                    }
+                    for (Map.Entry<String, List<ConfigChangeListener>> entry : configListenerMap.entrySet()) {
+                        String configId = entry.getKey();
+                        String currentConfig = getConfig(configId);
+                        if (null != currentConfig && currentConfig.equals(listenedConfigMap.get(configId))) {
+                            listenedConfigMap.put(configId, currentConfig);
+                            notifyAllListener(configId, configListenerMap.get(configId));
+
+                        } else if (null == currentConfig && null != listenedConfigMap.get(configId)) {
+                            notifyAllListener(configId, configListenerMap.get(configId));
+                        }
+                    }
+                    Thread.sleep(LISTENER_CONFIG_INTERNAL);
+                } catch (Exception exx) {
+                    LOGGER.error(exx.getMessage());
+                }
+
+            }
+        }
+
+        private void notifyAllListener(String dataId, List<ConfigChangeListener> configChangeListeners) {
+            List<ConfigChangeListener> needNotifyListeners = new ArrayList<>();
+            if (null != dataId && null != listener) {
+                needNotifyListeners.addAll(configChangeListeners);
+            } else {
+                for (ConfigChangeListener configChangeListener : configChangeListeners) {
+                    if (null == configChangeListener.getExecutor()) {
+                        needNotifyListeners.add(configChangeListener);
+                    }
+                }
+            }
+            for (ConfigChangeListener configChangeListener : needNotifyListeners) {
+                configChangeListener.receiveConfigInfo(listenedConfigMap.get(dataId));
+            }
+        }
+
+    }
+
 }
diff --git a/core/pom.xml b/core/pom.xml
index b69f2568d..42434e308 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -57,6 +57,11 @@
             <groupId>commons-pool</groupId>
             <artifactId>commons-pool</artifactId>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <extensions>
diff --git a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndRequest.java b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndRequest.java
index be008e279..b41b4a00a 100644
--- a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndRequest.java
+++ b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndRequest.java
@@ -16,10 +16,10 @@
 
 package com.alibaba.fescar.core.protocol.transaction;
 
-import java.nio.ByteBuffer;
-
 import com.alibaba.fescar.core.protocol.MergedMessage;
 
+import java.nio.ByteBuffer;
+
 public abstract class AbstractGlobalEndRequest extends AbstractTransactionRequestToTC implements MergedMessage {
 
     protected long transactionId;
@@ -72,4 +72,16 @@ public abstract class AbstractGlobalEndRequest extends AbstractTransactionReques
             this.setExtraData(new String(bs, UTF8));
         }
     }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("transactionId=");
+        result.append(transactionId);
+        result.append(",");
+        result.append("extraData=");
+        result.append(extraData);
+
+        return result.toString();
+    }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndResponse.java b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndResponse.java
index 28767a384..2e94e380e 100644
--- a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndResponse.java
+++ b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/AbstractGlobalEndResponse.java
@@ -16,10 +16,10 @@
 
 package com.alibaba.fescar.core.protocol.transaction;
 
-import java.nio.ByteBuffer;
-
 import com.alibaba.fescar.core.model.GlobalStatus;
 
+import java.nio.ByteBuffer;
+
 public abstract class AbstractGlobalEndResponse extends AbstractTransactionResponse {
 
     protected GlobalStatus globalStatus;
@@ -45,4 +45,18 @@ public abstract class AbstractGlobalEndResponse extends AbstractTransactionRespo
         globalStatus = GlobalStatus.get(byteBuffer.get());
     }
 
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("globalStatus=");
+        result.append(globalStatus);
+        result.append(",");
+        result.append("ResultCode=");
+        result.append(getResultCode());
+        result.append(",");
+        result.append("Msg=");
+        result.append(getMsg());
+
+        return result.toString();
+    }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/BranchReportRequest.java b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/BranchReportRequest.java
index 4fdc53cc3..896028554 100644
--- a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/BranchReportRequest.java
+++ b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/BranchReportRequest.java
@@ -16,12 +16,12 @@
 
 package com.alibaba.fescar.core.protocol.transaction;
 
-import java.nio.ByteBuffer;
-
 import com.alibaba.fescar.core.model.BranchStatus;
 import com.alibaba.fescar.core.protocol.MergedMessage;
 import com.alibaba.fescar.core.rpc.RpcContext;
 
+import java.nio.ByteBuffer;
+
 public class BranchReportRequest extends AbstractTransactionRequestToTC implements MergedMessage {
 
     private long transactionId;
@@ -146,4 +146,25 @@ public class BranchReportRequest extends AbstractTransactionRequestToTC implemen
     public AbstractTransactionResponse handle(RpcContext rpcContext) {
         return handler.handle(this, rpcContext);
     }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("transactionId=");
+        result.append(transactionId);
+        result.append(",");
+        result.append("branchId=");
+        result.append(branchId);
+        result.append(",");
+        result.append("resourceId=");
+        result.append(resourceId);
+        result.append(",");
+        result.append("status=");
+        result.append(status);
+        result.append(",");
+        result.append("applicationData=");
+        result.append(applicationData);
+
+        return result.toString();
+    }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginRequest.java b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginRequest.java
index c6f4d6faa..af589f9bd 100644
--- a/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginRequest.java
+++ b/core/src/main/java/com/alibaba/fescar/core/protocol/transaction/GlobalBeginRequest.java
@@ -16,11 +16,11 @@
 
 package com.alibaba.fescar.core.protocol.transaction;
 
-import java.nio.ByteBuffer;
-
 import com.alibaba.fescar.core.protocol.MergedMessage;
 import com.alibaba.fescar.core.rpc.RpcContext;
 
+import java.nio.ByteBuffer;
+
 public class GlobalBeginRequest extends AbstractTransactionRequestToTC implements MergedMessage {
 
     private static final long serialVersionUID = 7236162274218388376L;
@@ -87,4 +87,16 @@ public class GlobalBeginRequest extends AbstractTransactionRequestToTC implement
     public AbstractTransactionResponse handle(RpcContext rpcContext) {
         return handler.handle(this, rpcContext);
     }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("timeout=");
+        result.append(timeout);
+        result.append(",");
+        result.append("transactionName=");
+        result.append(transactionName);
+
+        return result.toString();
+    }
 }
diff --git a/core/src/main/java/com/alibaba/fescar/core/rpc/netty/AbstractRpcRemoting.java b/core/src/main/java/com/alibaba/fescar/core/rpc/netty/AbstractRpcRemoting.java
index 60bff51f2..6e8a0441b 100644
--- a/core/src/main/java/com/alibaba/fescar/core/rpc/netty/AbstractRpcRemoting.java
+++ b/core/src/main/java/com/alibaba/fescar/core/rpc/netty/AbstractRpcRemoting.java
@@ -96,7 +96,7 @@ public abstract class AbstractRpcRemoting extends ChannelDuplexHandler {
     /**
      * The Is sending.
      */
-    protected boolean isSending = false;
+    protected volatile boolean isSending = false;
     private String group = "DEFAULT";
     /**
      * The Merge msg map.
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 32db941b0..b646fbf1d 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
@@ -17,7 +17,6 @@
 package com.alibaba.fescar.tm.dubbo.impl;
 
 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.OrderService;
@@ -52,13 +51,7 @@ public class AccountServiceImpl implements AccountService {
         LOGGER.info("Account Service End ... ");
     }
 
-    public static void main(String[] args) throws Throwable {
-
-        String applicationId = "dubbo-demo-account-service";
-        String txServiceGroup = "my_test_tx_group";
-
-        RMClientAT.init(applicationId, txServiceGroup);
-
+    public static void main(String[] args) {
         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/OrderServiceImpl.java b/examples/src/main/java/com/alibaba/fescar/tm/dubbo/impl/OrderServiceImpl.java
index 2eaaf72c8..e56b4cdd7 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,7 +21,6 @@ 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;
@@ -103,16 +102,10 @@ public class OrderServiceImpl implements OrderService {
         return 200 * orderCount;
     }
 
-    public static void main(String[] args) throws Throwable {
-
-        String applicationId = "dubbo-demo-order-service";
-        String txServiceGroup = "my_test_tx_group";
-
-        RMClientAT.init(applicationId, txServiceGroup);
+    public static void main(String[] args) {
 
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-order-service.xml"});
         context.getBean("service");
-        JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
         new ApplicationKeeper(context).keep();
     }
 }
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 8f1839740..c1e403e37 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
@@ -17,7 +17,6 @@
 package com.alibaba.fescar.tm.dubbo.impl;
 
 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.StorageService;
 
@@ -45,23 +44,20 @@ 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);
+        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});
+        jdbcTemplate.update("update storage_tbl set count = count - ? where commodity_code = ?",
+            new Object[] {count, commodityCode});
         LOGGER.info("Storage Service End ... ");
 
     }
 
-    public static void main(String[] args) throws Throwable {
-
-        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"});
+    public static void main(String[] args) {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
+            new String[] {"dubbo-storage-service.xml"});
         context.getBean("service");
-        JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
+        JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplate");
         jdbcTemplate.update("delete from storage_tbl where commodity_code = 'C00321'");
         jdbcTemplate.update("insert into storage_tbl(commodity_code, count) values ('C00321', 100)");
         new ApplicationKeeper(context).keep();
diff --git a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/DataSourceManager.java b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/DataSourceManager.java
index 61dd4e961..682e2e436 100644
--- a/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/DataSourceManager.java
+++ b/rm-datasource/src/main/java/com/alibaba/fescar/rm/datasource/DataSourceManager.java
@@ -53,6 +53,7 @@ public class DataSourceManager implements ResourceManager {
             request.setTransactionId(XID.getTransactionId(xid));
             request.setLockKey(lockKeys);
             request.setResourceId(resourceId);
+            request.setBranchType(branchType);
 
             BranchRegisterResponse response = (BranchRegisterResponse) RmRpcClient.getInstance().sendMsgWithResponse(request);
             if (response.getResultCode() == ResultCode.Failed) {


Test patch:
diff --git a/config/src/test/java/com/alibaba/fescar/config/FileConfigurationTest.java b/config/src/test/java/com/alibaba/fescar/config/FileConfigurationTest.java
index cf1c61123..b932aebf8 100644
--- a/config/src/test/java/com/alibaba/fescar/config/FileConfigurationTest.java
+++ b/config/src/test/java/com/alibaba/fescar/config/FileConfigurationTest.java
@@ -16,89 +16,166 @@
 
 package com.alibaba.fescar.config;
 
+import java.util.concurrent.ExecutorService;
+
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
 /**
  * @Author: jimin.jm@alibaba-inc.com
- * @Project: fescar-all
- * @DateTime: 2018/12/21 14:03
+ * @Project: feats-all
+ * @DateTime: 2019/1/24 1:31 PM
  * @FileName: FileConfigurationTest
  * @Description:
  */
 public class FileConfigurationTest {
-
-    FileConfiguration fileConfiguration = new FileConfiguration();
-
-    @org.junit.Test
-    public void isRunAsClientProxy() throws Exception {
-
+    private static final Config CONFIG = ConfigFactory.load();
+    private static final Configuration FILE_CONFIG = ConfigurationFactory.getInstance();
+    private static final String INT_DATAID = "transport.thread-factory.client-selector-thread-size";
+    private static final String LONG_DATAID = "transport.thread-factory.worker-thread-size";
+    private static final String BOOLEAN_DATAID = "service.disable";
+    private static final String STRING_DATAID = "transport.type";
+    private static final String PUT_DATAID = "transport.mock";
+    private static final String NOT_EXIST_DATAID = "service.yyy.xxx";
+
+    @Test
+    public void testGetInt() {
+        Assert.assertEquals(FILE_CONFIG.getInt(INT_DATAID), CONFIG.getInt(INT_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getInt(NOT_EXIST_DATAID), 0);
     }
 
-    @org.junit.Test
-    public void isRunAsServerProxy() throws Exception {
-
+    @Test
+    public void testGetInt1() {
+        Assert.assertEquals(FILE_CONFIG.getInt(INT_DATAID, 999), CONFIG.getInt(INT_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getInt(NOT_EXIST_DATAID, 999), 999);
     }
 
-    @org.junit.Test
-    public void getConfig() throws Exception {
-
-        String server = fileConfiguration.getConfig("transport.server");
-        System.out.println(server);
-        //TRANSPORT_SERVER_TYPE = TransportServerType.valueOf(CONFIG.getConfig("transport.server"));
+    @Test
+    public void testGetInt2() {
+        Assert.assertEquals(FILE_CONFIG.getInt(INT_DATAID, 999, 1000), CONFIG.getInt(INT_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getInt(NOT_EXIST_DATAID, 999, 1000), 999);
     }
 
-    @org.junit.Test
-    public void getConfig1() throws Exception {
-        String shareBossWorker = fileConfiguration.getConfig("transport.thread-factory.share-boss-worker");
-        System.out.println(shareBossWorker);
+    @Test
+    public void testGetLong() {
+        Assert.assertEquals(FILE_CONFIG.getLong(LONG_DATAID), CONFIG.getLong(LONG_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getLong(NOT_EXIST_DATAID), 0);
     }
 
-    @org.junit.Test
-    public void getConfig2() throws Exception {
-
+    @Test
+    public void testGetLong1() {
+        Assert.assertEquals(FILE_CONFIG.getLong(LONG_DATAID, 999L), CONFIG.getLong(LONG_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getLong(NOT_EXIST_DATAID, 999L), 999L);
     }
 
-    @org.junit.Test
-    public void getConfig3() throws Exception {
-
+    @Test
+    public void testGetLong2() {
+        Assert.assertEquals(FILE_CONFIG.getLong(LONG_DATAID, 999L, 1000), CONFIG.getLong(LONG_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getLong(NOT_EXIST_DATAID, 999L, 1000), 999L);
     }
 
-    @org.junit.Test
-    public void putConfig() throws Exception {
-
+    @Test
+    public void testGetBoolean() {
+        Assert.assertEquals(FILE_CONFIG.getBoolean(BOOLEAN_DATAID), CONFIG.getBoolean(BOOLEAN_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getBoolean(NOT_EXIST_DATAID), false);
     }
 
-    @org.junit.Test
-    public void putConfig1() throws Exception {
+    @Test
+    public void testGetBoolean1() {
+        Assert.assertEquals(FILE_CONFIG.getBoolean(BOOLEAN_DATAID, true), CONFIG.getBoolean(BOOLEAN_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getBoolean(NOT_EXIST_DATAID, false), false);
+    }
 
+    @Test
+    public void testGetBoolean2() {
+        Assert.assertEquals(FILE_CONFIG.getBoolean(BOOLEAN_DATAID, true, 1000), CONFIG.getBoolean(BOOLEAN_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getBoolean(NOT_EXIST_DATAID, false, 1000), false);
     }
 
-    @org.junit.Test
-    public void putConfigIfAbsent() throws Exception {
+    @Test
+    public void testGetConfig() {
+        Assert.assertEquals(FILE_CONFIG.getConfig(STRING_DATAID), CONFIG.getString(STRING_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getConfig(NOT_EXIST_DATAID), null);
+    }
 
+    @Test
+    public void testGetConfig1() {
+        Assert.assertEquals(FILE_CONFIG.getConfig(STRING_DATAID, 1000), CONFIG.getString(STRING_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getConfig(NOT_EXIST_DATAID, 1000), null);
     }
 
-    @org.junit.Test
-    public void putConfigIfAbsent1() throws Exception {
+    @Test
+    public void testGetConfig2() {
+        Assert.assertEquals(FILE_CONFIG.getConfig(STRING_DATAID, "123"), CONFIG.getString(STRING_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getConfig(NOT_EXIST_DATAID, "123"), "123");
+    }
 
+    @Test
+    public void testGetConfig3() {
+        Assert.assertEquals(FILE_CONFIG.getConfig(STRING_DATAID, "123", 1000), CONFIG.getString(STRING_DATAID));
+        Assert.assertEquals(FILE_CONFIG.getConfig(NOT_EXIST_DATAID, "123", 1000), "123");
     }
 
-    @org.junit.Test
-    public void removeConfig() throws Exception {
+    @Test
+    public void testPutConfig() {
+        Assert.assertTrue(FILE_CONFIG.putConfig(PUT_DATAID, "123"));
+    }
 
+    @Test
+    public void testPutConfig1() {
+        Assert.assertTrue(FILE_CONFIG.putConfig(PUT_DATAID, "123", 5000));
     }
 
-    @org.junit.Test
-    public void removeConfig1() throws Exception {
+    @Test
+    public void testPutConfigIfAbsent() {
+        Assert.assertTrue(FILE_CONFIG.putConfigIfAbsent(PUT_DATAID, "123"));
+    }
 
+    @Test
+    public void testPutConfigIfAbsent1() {
+        Assert.assertTrue(FILE_CONFIG.putConfigIfAbsent(PUT_DATAID, "123", 5000));
     }
 
-    @org.junit.Test
-    public void addConfigListener() throws Exception {
+    @Test
+    public void testRemoveConfig() {
+        Assert.assertTrue(FILE_CONFIG.removeConfig(PUT_DATAID));
+    }
 
+    @Test
+    public void testRemoveConfig1() {
+        Assert.assertTrue(FILE_CONFIG.removeConfig(PUT_DATAID, 5000));
     }
 
-    @org.junit.Test
-    public void removeConfigListener() throws Exception {
+    @Test(dataProvider = "listenerProvider")
+    public void testAddConfigListener(ConfigChangeListener listener) {
+        FILE_CONFIG.addConfigListener(INT_DATAID, listener);
+        Assert.assertEquals(FILE_CONFIG.getConfigListeners(INT_DATAID).size(), 1);
+    }
 
+    @Test(dataProvider = "listenerProvider")
+    public void testRemoveConfigListener(ConfigChangeListener listener) {
+        int currSize = FILE_CONFIG.getConfigListeners(INT_DATAID).size();
+        FILE_CONFIG.addConfigListener(INT_DATAID, listener);
+        FILE_CONFIG.removeConfigListener(INT_DATAID, listener);
+        Assert.assertEquals(FILE_CONFIG.getConfigListeners(INT_DATAID).size(), currSize);
     }
 
-}
+    @DataProvider
+    public static Object[][] listenerProvider() {
+        ConfigChangeListener listener = new ConfigChangeListener() {
+            @Override
+            public ExecutorService getExecutor() {
+                return null;
+            }
+
+            @Override
+            public void receiveConfigInfo(String configInfo) {
+                System.out.print(configInfo);
+            }
+        };
+        return new Object[][] {{listener}};
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/com/alibaba/fescar/core/message/BranchReportRequestTest.java b/core/src/test/java/com/alibaba/fescar/core/message/BranchReportRequestTest.java
new file mode 100644
index 000000000..ccc6b0422
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fescar/core/message/BranchReportRequestTest.java
@@ -0,0 +1,92 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.core.message;
+
+import com.alibaba.fescar.core.model.BranchStatus;
+import com.alibaba.fescar.core.protocol.transaction.BranchReportRequest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * @author xiajun.0706@163.com
+ * @since 2019/1/24
+ */
+public class BranchReportRequestTest {
+
+  @Test
+  public void testToString() throws Exception {
+    BranchReportRequest branchReportRequest = new BranchReportRequest();
+
+    branchReportRequest.setTransactionId(1249853);
+    branchReportRequest.setBranchId(3);
+    branchReportRequest.setResourceId("resource003");
+    branchReportRequest.setStatus(BranchStatus.PhaseOne_Timeout);
+    branchReportRequest.setApplicationData("test app data");
+
+    Assert.assertEquals("transactionId=1249853,branchId=3,resourceId=resource003,status=PhaseOne_Timeout,applicationData=test app data",
+                        branchReportRequest.toString());
+  }
+
+
+  @Test
+  public void testEncode() throws Exception{
+    BranchReportRequest branchReportRequest = new BranchReportRequest();
+
+    branchReportRequest.setTransactionId(1249853);
+    branchReportRequest.setBranchId(3);
+    branchReportRequest.setResourceId("resource003");
+    branchReportRequest.setStatus(BranchStatus.PhaseOne_Timeout);
+    branchReportRequest.setApplicationData("test app data");
+
+    byte[] encodeResult = branchReportRequest.encode();
+    System.out.println(encodeResult);
+    String encodeResultStr = Arrays.toString(encodeResult);
+    System.out.println(encodeResultStr);
+
+
+    Assert.assertEquals("[0, 0, 0, 0, 0, 19, 18, 61, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 11, 114, 101, 115, 111, 117, 114, 99, 101, 48, 48, 51, 0, 0, 0, 13, 116, 101, 115, 116, 32, 97, 112, 112, 32, 100, 97, 116, 97]", encodeResultStr);
+  }
+
+  @Test
+  public void testDecode() throws Exception{
+    BranchReportRequest branchReportRequest = new BranchReportRequest();
+
+    branchReportRequest.setTransactionId(1249853);
+    branchReportRequest.setBranchId(3);
+    branchReportRequest.setResourceId("resource003");
+    branchReportRequest.setStatus(BranchStatus.PhaseOne_Timeout);
+    branchReportRequest.setApplicationData("test app data");
+
+    byte[] encodeResult = branchReportRequest.encode();
+
+    ByteBuffer byteBuffer = ByteBuffer.allocate(encodeResult.length);
+    byteBuffer.put(encodeResult);
+    byteBuffer.flip();
+
+    BranchReportRequest decodeBranchReportRequest = new BranchReportRequest();
+    decodeBranchReportRequest.decode(byteBuffer);
+    System.out.println(decodeBranchReportRequest);
+    Assert.assertEquals(branchReportRequest.getTransactionId(), decodeBranchReportRequest.getTransactionId());
+    Assert.assertEquals(branchReportRequest.getBranchId(), decodeBranchReportRequest.getBranchId());
+    Assert.assertEquals(branchReportRequest.getResourceId(), decodeBranchReportRequest.getResourceId());
+    Assert.assertEquals(branchReportRequest.getStatus(), decodeBranchReportRequest.getStatus());
+    Assert.assertEquals(branchReportRequest.getApplicationData(), decodeBranchReportRequest.getApplicationData());
+  }
+}
\ No newline at end of file
diff --git a/core/src/test/java/com/alibaba/fescar/core/message/GlobalBeginRequestTest.java b/core/src/test/java/com/alibaba/fescar/core/message/GlobalBeginRequestTest.java
new file mode 100644
index 000000000..0d6ec9ed1
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fescar/core/message/GlobalBeginRequestTest.java
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.core.message;
+
+import com.alibaba.fescar.core.protocol.transaction.GlobalBeginRequest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * @author xiajun.0706@163.com
+ * @since 2019/1/24
+ */
+public class GlobalBeginRequestTest {
+
+  @Test
+  public void testToString() throws Exception{
+    GlobalBeginRequest globalBeginRequest = new GlobalBeginRequest();
+    globalBeginRequest.setTransactionName("tran 1");
+    System.out.println(globalBeginRequest.toString());
+
+    Assert.assertEquals("timeout=60000,transactionName=tran 1", globalBeginRequest.toString());
+  }
+
+  @Test
+  public void testEncode() throws Exception{
+    GlobalBeginRequest globalBeginRequest = new GlobalBeginRequest();
+    globalBeginRequest.setTransactionName("tran 1");
+
+    byte[] encodeResult = globalBeginRequest.encode();
+    String encodeResultStr = Arrays.toString(encodeResult);
+
+    Assert.assertEquals("[0, 0, -22, 96, 0, 6, 116, 114, 97, 110, 32, 49]", encodeResultStr);
+  }
+
+  @Test
+  public void testDecode() throws Exception{
+    GlobalBeginRequest globalBeginRequest = new GlobalBeginRequest();
+    globalBeginRequest.setTransactionName("tran 1");
+
+    byte[] encodeResult = globalBeginRequest.encode();
+
+    ByteBuffer byteBuffer = ByteBuffer.allocate(encodeResult.length);
+    byteBuffer.put(encodeResult);
+    byteBuffer.flip();
+
+    GlobalBeginRequest decodeGlobalBeginRequest = new GlobalBeginRequest();
+    decodeGlobalBeginRequest.decode(byteBuffer);
+    System.out.println(decodeGlobalBeginRequest);
+    Assert.assertEquals(globalBeginRequest.getTimeout(), decodeGlobalBeginRequest.getTimeout());
+    Assert.assertEquals(globalBeginRequest.getTransactionName(), decodeGlobalBeginRequest.getTransactionName());
+  }
+}
\ No newline at end of file
diff --git a/core/src/test/java/com/alibaba/fescar/core/message/GlobalCommitResponseTest.java b/core/src/test/java/com/alibaba/fescar/core/message/GlobalCommitResponseTest.java
new file mode 100644
index 000000000..c0d2bc22f
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fescar/core/message/GlobalCommitResponseTest.java
@@ -0,0 +1,117 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.core.message;
+
+import com.alibaba.fescar.core.model.GlobalStatus;
+import com.alibaba.fescar.core.protocol.ResultCode;
+import com.alibaba.fescar.core.protocol.transaction.GlobalCommitResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * @author xiajun.0706@163.com
+ * @since 2019/1/24
+ */
+public class GlobalCommitResponseTest {
+
+  @Test
+  public void testToString() throws Exception{
+    GlobalCommitResponse globalCommitResponse = new GlobalCommitResponse();
+
+    globalCommitResponse.setGlobalStatus(GlobalStatus.Committed);
+    globalCommitResponse.setResultCode(ResultCode.Success);
+    globalCommitResponse.setMsg("OK");
+
+    System.out.println(globalCommitResponse.toString());
+
+    Assert.assertEquals("globalStatus=Committed,ResultCode=Success,Msg=OK", globalCommitResponse.toString());
+  }
+
+  @Test
+  public void testEncode() throws Exception{
+    GlobalCommitResponse globalCommitResponse = new GlobalCommitResponse();
+
+    globalCommitResponse.setGlobalStatus(GlobalStatus.Committed);
+    globalCommitResponse.setResultCode(ResultCode.Success);
+    globalCommitResponse.setMsg("OK");
+
+    System.out.println(globalCommitResponse.toString());
+
+    byte[] encodeResult = globalCommitResponse.encode();
+    System.out.println(encodeResult);
+    String encodeResultStr = Arrays.toString(encodeResult);
+    System.out.println(encodeResultStr);
+
+
+    Assert.assertEquals("[1, 0, 8]", encodeResultStr);
+  }
+
+  @Test
+  public void testDecodeNormal() throws Exception{
+    GlobalCommitResponse globalCommitResponse = new GlobalCommitResponse();
+
+    globalCommitResponse.setGlobalStatus(GlobalStatus.Committed);
+    globalCommitResponse.setResultCode(ResultCode.Success);
+    globalCommitResponse.setMsg("OK");
+
+    System.out.println(globalCommitResponse.toString());
+
+    byte[] encodeResult = globalCommitResponse.encode();
+
+    ByteBuffer byteBuffer = ByteBuffer.allocate(encodeResult.length);
+    byteBuffer.put(encodeResult);
+    byteBuffer.flip();
+    GlobalCommitResponse decodeGlobalCommitResponse = new GlobalCommitResponse();
+    decodeGlobalCommitResponse.decode(byteBuffer);
+
+    System.out.println(decodeGlobalCommitResponse.toString());
+
+    Assert.assertEquals(globalCommitResponse.getGlobalStatus(), decodeGlobalCommitResponse.getGlobalStatus());
+    Assert.assertEquals(globalCommitResponse.getResultCode(), decodeGlobalCommitResponse.getResultCode());
+    //success response do not have msg
+    Assert.assertTrue(StringUtils.isBlank(decodeGlobalCommitResponse.getMsg()));
+  }
+
+  @Test
+  public void testDecodeException() throws Exception{
+    GlobalCommitResponse globalCommitResponse = new GlobalCommitResponse();
+
+    globalCommitResponse.setGlobalStatus(GlobalStatus.CommitFailed);
+    globalCommitResponse.setResultCode(ResultCode.Failed);
+    globalCommitResponse.setMsg("error happened");
+
+    System.out.println(globalCommitResponse.toString());
+
+    byte[] encodeResult = globalCommitResponse.encode();
+
+    ByteBuffer byteBuffer = ByteBuffer.allocate(encodeResult.length);
+    byteBuffer.put(encodeResult);
+    byteBuffer.flip();
+    GlobalCommitResponse decodeGlobalCommitResponse = new GlobalCommitResponse();
+    decodeGlobalCommitResponse.decode(byteBuffer);
+
+    System.out.println(decodeGlobalCommitResponse.toString());
+
+    Assert.assertEquals(globalCommitResponse.getGlobalStatus(), decodeGlobalCommitResponse.getGlobalStatus());
+    Assert.assertEquals(globalCommitResponse.getResultCode(), decodeGlobalCommitResponse.getResultCode());
+    Assert.assertEquals(globalCommitResponse.getMsg(), decodeGlobalCommitResponse.getMsg());
+  }
+}
\ No newline at end of file
diff --git a/core/src/test/java/com/alibaba/fescar/core/message/GlobalRollbackRequestTest.java b/core/src/test/java/com/alibaba/fescar/core/message/GlobalRollbackRequestTest.java
new file mode 100644
index 000000000..23217ee02
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fescar/core/message/GlobalRollbackRequestTest.java
@@ -0,0 +1,81 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.core.message;
+
+import com.alibaba.fescar.core.protocol.transaction.GlobalRollbackRequest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * @author xiajun.0706@163.com
+ * @since 2019/1/24
+ */
+public class GlobalRollbackRequestTest {
+
+  @Test
+  public void testToString() throws Exception{
+    GlobalRollbackRequest globalRollbackRequest = new GlobalRollbackRequest();
+
+    globalRollbackRequest.setTransactionId(1249853);
+    globalRollbackRequest.setExtraData("test_extra_data");
+
+    System.out.println(globalRollbackRequest.toString());
+
+    Assert.assertEquals("transactionId=1249853,extraData=test_extra_data", globalRollbackRequest.toString());
+  }
+
+  @Test
+  public void testEncode() throws Exception{
+    GlobalRollbackRequest globalRollbackRequest = new GlobalRollbackRequest();
+
+    globalRollbackRequest.setTransactionId(1249853);
+    globalRollbackRequest.setExtraData("test_extra_data");
+
+    System.out.println(globalRollbackRequest.toString());
+
+    byte[] encodeResult = globalRollbackRequest.encode();
+    System.out.println(encodeResult);
+    String encodeResultStr = Arrays.toString(encodeResult);
+    System.out.println(encodeResultStr);
+
+
+    Assert.assertEquals("[0, 0, 0, 0, 0, 19, 18, 61, 0, 15, 116, 101, 115, 116, 95, 101, 120, 116, 114, 97, 95, 100, 97, 116, 97]", encodeResultStr);
+  }
+
+  @Test
+  public void testDecode() throws Exception{
+    GlobalRollbackRequest globalRollbackRequest = new GlobalRollbackRequest();
+
+    globalRollbackRequest.setTransactionId(1249853L);
+    globalRollbackRequest.setExtraData("test_extra_data");
+
+    byte[] encodeResult = globalRollbackRequest.encode();
+
+    ByteBuffer byteBuffer = ByteBuffer.allocate(encodeResult.length);
+    byteBuffer.put(encodeResult);
+    byteBuffer.flip();
+
+    GlobalRollbackRequest decodeGlobalRollbackRequest = new GlobalRollbackRequest();
+    decodeGlobalRollbackRequest.decode(byteBuffer);
+    System.out.println(decodeGlobalRollbackRequest);
+    Assert.assertEquals(globalRollbackRequest.getTransactionId(), decodeGlobalRollbackRequest.getTransactionId());
+    Assert.assertEquals(globalRollbackRequest.getExtraData(), decodeGlobalRollbackRequest.getExtraData());
+  }
+}
\ No newline at end of file
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/AbstractMySQLRecognizerTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/AbstractMySQLRecognizerTest.java
new file mode 100644
index 000000000..4f563bcd2
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/AbstractMySQLRecognizerTest.java
@@ -0,0 +1,19 @@
+package com.alibaba.fescar.rm.datasource.sql.druid;
+
+import com.alibaba.druid.sql.SQLUtils;
+import com.alibaba.druid.sql.ast.SQLStatement;
+
+import java.util.List;
+
+/**
+ * @author hanwen
+ * created at 2019-01-25
+ */
+public class AbstractMySQLRecognizerTest {
+
+    public SQLStatement getSQLStatement(String sql) {
+        List<SQLStatement> stats = SQLUtils.parseStatements(sql, "mysql");
+        return stats.get(0);
+    }
+
+}
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLDeleteRecognizerTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLDeleteRecognizerTest.java
new file mode 100644
index 000000000..c5622e69a
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLDeleteRecognizerTest.java
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.rm.datasource.sql.druid;
+
+import com.alibaba.druid.sql.ast.SQLStatement;
+import com.alibaba.fescar.rm.datasource.ParametersHolder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * @author hanwen
+ * created at 2019-01-25
+ */
+public class MySQLDeleteRecognizerTest extends AbstractMySQLRecognizerTest {
+
+    @Test
+    public void deleteRecognizerTest_0() {
+
+        String sql = "DELETE FROM t1 WHERE id = 'id1'";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLDeleteRecognizer mySQLDeleteRecognizer = new MySQLDeleteRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLDeleteRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLDeleteRecognizer.getTableName());
+        Assert.assertEquals("id = 'id1'", mySQLDeleteRecognizer.getWhereCondition());
+    }
+
+    @Test
+    public void deleteRecognizerTest_1() {
+
+        String sql = "DELETE FROM t1 WHERE id = ?";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLDeleteRecognizer mySQLDeleteRecognizer = new MySQLDeleteRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLDeleteRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLDeleteRecognizer.getTableName());
+
+        // test overflow parameters
+        ArrayList<Object> paramAppender = new ArrayList<>();
+        String whereCondition = mySQLDeleteRecognizer.getWhereCondition(new ParametersHolder() {
+            @Override
+            public ArrayList<Object>[] getParameters() {
+                ArrayList<Object> idParam = new ArrayList<>();
+                idParam.add("id1");
+                ArrayList<Object> nameParam = new ArrayList<>();
+                nameParam.add("name1");
+                return new ArrayList[]{idParam, nameParam};
+            }
+        }, paramAppender);
+
+        Assert.assertEquals(Collections.singletonList("id1"), paramAppender);
+        Assert.assertEquals("id = ?", whereCondition);
+    }
+}
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLInsertRecognizerTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLInsertRecognizerTest.java
new file mode 100644
index 000000000..eeb6eff43
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLInsertRecognizerTest.java
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.rm.datasource.sql.druid;
+
+import com.alibaba.druid.sql.ast.SQLStatement;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @author hanwen
+ * created at 2019-01-25
+ */
+public class MySQLInsertRecognizerTest extends AbstractMySQLRecognizerTest {
+
+    @Test
+    public void insertRecognizerTest_0() {
+
+        String sql = "INSERT INTO t1 (name) VALUES ('name1')";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLInsertRecognizer mySQLInsertRecognizer = new MySQLInsertRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLInsertRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLInsertRecognizer.getTableName());
+        Assert.assertEquals(Collections.singletonList("name"), mySQLInsertRecognizer.getInsertColumns());
+        Assert.assertEquals(1, mySQLInsertRecognizer.getInsertRows().size());
+        Assert.assertEquals(Collections.singletonList("name1"), mySQLInsertRecognizer.getInsertRows().get(0));
+    }
+
+    @Test
+    public void insertRecognizerTest_1() {
+
+        String sql = "INSERT INTO t1 (name1, name2) VALUES ('name1', 'name2')";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLInsertRecognizer mySQLInsertRecognizer = new MySQLInsertRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLInsertRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLInsertRecognizer.getTableName());
+        Assert.assertEquals(Arrays.asList("name1", "name2"), mySQLInsertRecognizer.getInsertColumns());
+        Assert.assertEquals(1, mySQLInsertRecognizer.getInsertRows().size());
+        Assert.assertEquals(Arrays.asList("name1", "name2"), mySQLInsertRecognizer.getInsertRows().get(0));
+    }
+
+    @Test
+    public void insertRecognizerTest_3() {
+
+        String sql = "INSERT INTO t1 (name1, name2) VALUES ('name1', 'name2'), ('name3', 'name4'), ('name5', 'name6')";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLInsertRecognizer mySQLInsertRecognizer = new MySQLInsertRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLInsertRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLInsertRecognizer.getTableName());
+        Assert.assertEquals(Arrays.asList("name1", "name2"), mySQLInsertRecognizer.getInsertColumns());
+        Assert.assertEquals(3, mySQLInsertRecognizer.getInsertRows().size());
+        Assert.assertEquals(Arrays.asList("name1", "name2"), mySQLInsertRecognizer.getInsertRows().get(0));
+        Assert.assertEquals(Arrays.asList("name3", "name4"), mySQLInsertRecognizer.getInsertRows().get(1));
+        Assert.assertEquals(Arrays.asList("name5", "name6"), mySQLInsertRecognizer.getInsertRows().get(2));
+    }
+
+}
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLSelectForUpdateRecognizerTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLSelectForUpdateRecognizerTest.java
new file mode 100644
index 000000000..636d659fa
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLSelectForUpdateRecognizerTest.java
@@ -0,0 +1,97 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.rm.datasource.sql.druid;
+
+import com.alibaba.druid.sql.ast.SQLStatement;
+import com.alibaba.fescar.rm.datasource.ParametersHolder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class MySQLSelectForUpdateRecognizerTest extends AbstractMySQLRecognizerTest {
+
+    @Test
+    public void selectForUpdateRecognizerTest_0() {
+
+        String sql = "SELECT name FROM t1 WHERE id = 'id1' FOR UPDATE";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLSelectForUpdateRecognizer mySQLUpdateRecognizer = new MySQLSelectForUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+        Assert.assertEquals("id = 'id1'", mySQLUpdateRecognizer.getWhereCondition());
+    }
+
+    @Test
+    public void selectForUpdateRecognizerTest_1() {
+
+        String sql = "SELECT name FROM t1 WHERE id = ? FOR UPDATE";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLSelectForUpdateRecognizer mySQLUpdateRecognizer = new MySQLSelectForUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+
+        ArrayList<Object> paramAppender = new ArrayList<>();
+        String whereCondition = mySQLUpdateRecognizer.getWhereCondition(new ParametersHolder() {
+            @Override
+            public ArrayList<Object>[] getParameters() {
+                ArrayList<Object> idParam = new ArrayList<>();
+                idParam.add("id1");
+                return new ArrayList[]{idParam};
+            }
+        }, paramAppender);
+
+        Assert.assertEquals(Collections.singletonList("id1"), paramAppender);
+        Assert.assertEquals("id = ?", whereCondition);
+    }
+
+    @Test
+    public void selectForUpdateRecognizerTest_3() {
+
+        String sql = "SELECT name1, name2 FROM t1 WHERE id1 = ? FOR UPDATE";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLSelectForUpdateRecognizer mySQLUpdateRecognizer = new MySQLSelectForUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+
+        // test overflow parameters
+        ArrayList<Object> paramAppender = new ArrayList<>();
+        String whereCondition = mySQLUpdateRecognizer.getWhereCondition(new ParametersHolder() {
+            @Override
+            public ArrayList<Object>[] getParameters() {
+                ArrayList<Object> id1Param = new ArrayList<>();
+                id1Param.add("id1");
+                ArrayList<Object> id2Param = new ArrayList<>();
+                id2Param.add("id2");
+                return new ArrayList[]{id1Param, id2Param};
+            }
+        }, paramAppender);
+
+        Assert.assertEquals(Collections.singletonList("id1"), paramAppender);
+        Assert.assertEquals("id1 = ?", whereCondition);
+    }
+}
diff --git a/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLUpdateRecognizerTest.java b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLUpdateRecognizerTest.java
new file mode 100644
index 000000000..64ce9719b
--- /dev/null
+++ b/rm-datasource/src/test/java/com/alibaba/fescar/rm/datasource/sql/druid/MySQLUpdateRecognizerTest.java
@@ -0,0 +1,96 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.rm.datasource.sql.druid;
+
+import com.alibaba.druid.sql.ast.SQLStatement;
+import com.alibaba.fescar.rm.datasource.ParametersHolder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class MySQLUpdateRecognizerTest extends AbstractMySQLRecognizerTest {
+
+    @Test
+    public void updateRecognizerTest_0() {
+
+        String sql = "UPDATE t1 SET name = 'name1' WHERE id = 'id1'";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLUpdateRecognizer mySQLUpdateRecognizer = new MySQLUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+        Assert.assertEquals(1, mySQLUpdateRecognizer.getUpdateColumns().size());
+        Assert.assertEquals("name", mySQLUpdateRecognizer.getUpdateColumns().get(0));
+        Assert.assertEquals("name1", mySQLUpdateRecognizer.getUpdateValues().get(0));
+        Assert.assertEquals("id = 'id1'", mySQLUpdateRecognizer.getWhereCondition());
+    }
+
+    @Test
+    public void updateRecognizerTest_1() {
+
+        String sql = "UPDATE t1 SET name1 = 'name1', name2 = 'name2' WHERE id = 'id1'";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLUpdateRecognizer mySQLUpdateRecognizer = new MySQLUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+        Assert.assertEquals(2, mySQLUpdateRecognizer.getUpdateColumns().size());
+        Assert.assertEquals("name1", mySQLUpdateRecognizer.getUpdateColumns().get(0));
+        Assert.assertEquals("name1", mySQLUpdateRecognizer.getUpdateValues().get(0));
+        Assert.assertEquals("name2", mySQLUpdateRecognizer.getUpdateColumns().get(1));
+        Assert.assertEquals("name2", mySQLUpdateRecognizer.getUpdateValues().get(1));
+        Assert.assertEquals("id = 'id1'", mySQLUpdateRecognizer.getWhereCondition());
+    }
+
+    @Test
+    public void updateRecognizerTest_2() {
+
+        String sql = "UPDATE t1 SET name1 = 'name1', name2 = 'name2' WHERE id = ?";
+
+        SQLStatement statement = getSQLStatement(sql);
+
+        MySQLUpdateRecognizer mySQLUpdateRecognizer = new MySQLUpdateRecognizer(sql, statement);
+
+        Assert.assertEquals(sql, mySQLUpdateRecognizer.getOriginalSQL());
+        Assert.assertEquals("t1", mySQLUpdateRecognizer.getTableName());
+        Assert.assertEquals(2, mySQLUpdateRecognizer.getUpdateColumns().size());
+        Assert.assertEquals("name1", mySQLUpdateRecognizer.getUpdateColumns().get(0));
+        Assert.assertEquals("name1", mySQLUpdateRecognizer.getUpdateValues().get(0));
+        Assert.assertEquals("name2", mySQLUpdateRecognizer.getUpdateColumns().get(1));
+        Assert.assertEquals("name2", mySQLUpdateRecognizer.getUpdateValues().get(1));
+
+        ArrayList<Object> paramAppender = new ArrayList<>();
+        String whereCondition = mySQLUpdateRecognizer.getWhereCondition(new ParametersHolder() {
+            @Override
+            public ArrayList<Object>[] getParameters() {
+                ArrayList<Object> idParam = new ArrayList<>();
+                idParam.add("id1");
+                return new ArrayList[]{idParam};
+            }
+        }, paramAppender);
+
+        Assert.assertEquals(Collections.singletonList("id1"), paramAppender);
+
+        Assert.assertEquals("id = ?", whereCondition);
+    }
+}
diff --git a/server/src/test/java/com/alibaba/fescar/server/coordinator/DefaultCoreTest.java b/server/src/test/java/com/alibaba/fescar/server/coordinator/DefaultCoreTest.java
new file mode 100644
index 000000000..407454917
--- /dev/null
+++ b/server/src/test/java/com/alibaba/fescar/server/coordinator/DefaultCoreTest.java
@@ -0,0 +1,141 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.server.coordinator;
+
+import com.alibaba.fescar.common.XID;
+import com.alibaba.fescar.core.model.BranchStatus;
+import com.alibaba.fescar.core.model.BranchType;
+import com.alibaba.fescar.core.model.GlobalStatus;
+import com.alibaba.fescar.server.session.BranchSession;
+import com.alibaba.fescar.server.session.GlobalSession;
+import com.alibaba.fescar.server.session.SessionHolder;
+import org.testng.Assert;
+import org.testng.annotations.*;
+
+import java.util.Collection;
+
+/**
+ * @author zhimo.xiao@gmail.com
+ * @since 2019/1/23
+ */
+
+public class DefaultCoreTest {
+
+    private static Core core = new DefaultCore();
+
+    private static final String applicationId = "demo-child-app";
+
+    private static final String txServiceGroup = "my_test_tx_group";
+
+    private static final String txName = "tx-1";
+
+    private static final int timeout = 3000;
+
+    private static final String resourceId = "tb_1";
+
+    private static final String clientId = "c_1";
+
+    private static final String lockKeys_1 = "tb_1:11";
+
+    private static final String lockKeys_2 = "tb_1:12";
+
+    private static final String applicationData = "{\"data\":\"test\"}";
+
+    @BeforeTest
+    public void initSessionManager() throws Exception {
+        SessionHolder.init(null);
+    }
+
+    @Test(dataProvider = "xidProvider")
+    public void branchRegisterTest(String xid) throws Exception {
+        core.branchRegister(BranchType.AT, resourceId, clientId, xid, lockKeys_1);
+
+        long transactionId = XID.getTransactionId(xid);
+        GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId);
+        Assert.assertEquals(globalSession.getSortedBranches().size(), 1);
+    }
+
+    @Test(dataProvider = "xidAndBranchIdProvider")
+    public void branchReportTest(String xid, Long branchId) throws Exception {
+        core.branchReport(xid, branchId, BranchStatus.PhaseOne_Done, applicationData);
+
+        long transactionId = XID.getTransactionId(xid);
+        GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId);
+        BranchSession branchSession = globalSession.getBranch(branchId);
+        Assert.assertEquals(branchSession.getStatus(), BranchStatus.PhaseOne_Done);
+    }
+
+    @Test
+    public void beginTest() throws Exception {
+        String xid = core.begin(applicationId, txServiceGroup, txName, timeout);
+
+        long transactionId = XID.getTransactionId(xid);
+        GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId);
+        Assert.assertNotNull(globalSession);
+    }
+
+    @Test(dataProvider = "xidProvider")
+    public void commitTest(String xid) throws Exception {
+        GlobalStatus globalStatus = core.commit(xid);
+        Assert.assertEquals(globalStatus, GlobalStatus.Begin);
+    }
+
+    @Test(dataProvider = "xidProvider")
+    public void doGlobalCommitTest(String xid) throws Exception {
+        GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid));
+        core.doGlobalCommit(globalSession, false);
+        Assert.assertEquals(globalSession.getStatus(), GlobalStatus.Committed);
+    }
+
+    @Test(dataProvider = "xidProvider")
+    public void rollBackTest(String xid) throws Exception {
+        GlobalStatus globalStatus = core.rollback(xid);
+        Assert.assertEquals(globalStatus, GlobalStatus.Rollbacked);
+    }
+
+    @Test(dataProvider = "xidProvider")
+    public void doGlobalRollBackTest(String xid) throws Exception {
+        GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid));
+        core.doGlobalRollback(globalSession, false);
+        Assert.assertEquals(globalSession.getStatus(), GlobalStatus.Rollbacked);
+    }
+
+    @DataProvider
+    public static Object[][] xidProvider() throws Exception {
+        String xid = core.begin(applicationId, txServiceGroup, txName, timeout);
+        return new Object[][]{{xid}};
+    }
+
+    @DataProvider
+    public static Object[][] xidAndBranchIdProvider() throws Exception {
+        String xid = core.begin(applicationId, txServiceGroup, txName, timeout);
+        Long branchId = core.branchRegister(BranchType.AT, resourceId, clientId, xid, lockKeys_2);
+        return new Object[][]{{xid, branchId}};
+    }
+
+    @AfterTest
+    public void releaseSessionManager() throws Exception {
+        Collection<GlobalSession> globalSessions = SessionHolder.getRootSessionManager().allSessions();
+        Collection<GlobalSession> asyncGlobalSessions = SessionHolder.getAsyncCommittingSessionManager().allSessions();
+        for (GlobalSession asyncGlobalSession : asyncGlobalSessions) {
+            asyncGlobalSession.closeAndClean();
+        }
+        for (GlobalSession globalSession : globalSessions) {
+            globalSession.closeAndClean();
+        }
+    }
+
+}
diff --git a/server/src/test/java/com/alibaba/fescar/server/lock/DefaultLockManagerImplTest.java b/server/src/test/java/com/alibaba/fescar/server/lock/DefaultLockManagerImplTest.java
new file mode 100644
index 000000000..64696266c
--- /dev/null
+++ b/server/src/test/java/com/alibaba/fescar/server/lock/DefaultLockManagerImplTest.java
@@ -0,0 +1,70 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.server.lock;
+
+import com.alibaba.fescar.core.model.BranchType;
+import com.alibaba.fescar.server.UUIDGenerator;
+import com.alibaba.fescar.server.session.BranchSession;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * @author zhimo.xiao@gmail.com
+ * @since 2019/1/23
+ */
+
+public class DefaultLockManagerImplTest {
+
+    private LockManager lockManager = new DefaultLockManagerImpl();
+
+    private static final long transactionId = UUIDGenerator.generateUUID();
+
+    private static final String resourceId = "tb_1";
+
+    private static final String lockKey = "tb_1:13";
+
+    @Test(dataProvider = "branchSessionProvider")
+    public void acquireLockTest(BranchSession branchSession) throws Exception {
+        boolean result = lockManager.acquireLock(branchSession);
+        Assert.assertTrue(result);
+        branchSession.unlock();
+    }
+
+    @Test
+    public void isLockableTest() throws Exception {
+        boolean resultOne = lockManager.isLockable(transactionId, resourceId, lockKey);
+        Assert.assertTrue(resultOne);
+    }
+
+    @DataProvider
+    public static Object[][] branchSessionProvider() {
+        BranchSession branchSession = new BranchSession();
+        branchSession.setBranchId(1L);
+        branchSession.setTransactionId(transactionId);
+        branchSession.setClientId("c1");
+        branchSession.setResourceGroupId("my_test_tx_group");
+        branchSession.setResourceId(resourceId);
+        branchSession.setLockKey(lockKey);
+        branchSession.setBranchType(BranchType.AT);
+        branchSession.setApplicationId("demo-child-app");
+        branchSession.setTxServiceGroup("my_test_tx_group");
+        branchSession.setApplicationData("{\"data\":\"test\"}");
+        branchSession.setBranchType(BranchType.AT);
+        return new Object[][]{{branchSession}};
+    }
+
+}
diff --git a/server/src/test/java/com/alibaba/fescar/server/lock/LockManagerTest.java b/server/src/test/java/com/alibaba/fescar/server/lock/LockManagerTest.java
new file mode 100644
index 000000000..aa32984e7
--- /dev/null
+++ b/server/src/test/java/com/alibaba/fescar/server/lock/LockManagerTest.java
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.server.lock;
+
+import com.alibaba.fescar.core.model.BranchType;
+import com.alibaba.fescar.server.UUIDGenerator;
+import com.alibaba.fescar.server.session.BranchSession;
+import org.junit.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * @author tianming.xm@gmail.com
+ * @since 2019/1/23
+ */
+public class LockManagerTest {
+
+    @Test(dataProvider = "branchSessionProvider")
+    public void acquireLock_success(BranchSession branchSession) throws Exception{
+        LockManager lockManager = LockManagerFactory.get();
+        Assert.assertTrue(lockManager.acquireLock(branchSession));
+    }
+
+    @Test(dataProvider = "branchSessionsProvider")
+    public void acquireLock_failed(BranchSession branchSession1,BranchSession branchSession2) throws Exception{
+        LockManager lockManager = LockManagerFactory.get();
+        Assert.assertTrue(lockManager.acquireLock(branchSession1));
+        Assert.assertFalse(lockManager.acquireLock(branchSession2));
+    }
+
+    @Test(dataProvider = "branchSessionProvider")
+    public void isLockableTest(BranchSession branchSession) throws Exception{
+        branchSession.setLockKey("t:4");
+        LockManager lockManager = LockManagerFactory.get();
+        Assert.assertTrue(lockManager.isLockable(branchSession.getTransactionId(),branchSession.getResourceId(),branchSession.getLockKey()));
+        lockManager.acquireLock(branchSession);
+        branchSession.setTransactionId(UUIDGenerator.generateUUID());
+        Assert.assertFalse(lockManager.isLockable(branchSession.getTransactionId(),branchSession.getResourceId(),branchSession.getLockKey()));
+    }
+
+    @DataProvider
+    public static Object[][] branchSessionProvider() {
+        BranchSession branchSession = new BranchSession();
+        branchSession.setTransactionId(UUIDGenerator.generateUUID());
+        branchSession.setBranchId(0L);
+        branchSession.setClientId("c1");
+        branchSession.setResourceGroupId("my_test_tx_group");
+        branchSession.setResourceId("tb_1");
+        branchSession.setLockKey("t:0");
+        branchSession.setBranchType(BranchType.AT);
+        branchSession.setApplicationId("demo-child-app");
+        branchSession.setTxServiceGroup("my_test_tx_group");
+        branchSession.setApplicationData("{\"data\":\"test\"}");
+        branchSession.setBranchType(BranchType.AT);
+        return new Object[][] {{ branchSession}};
+    }
+
+    @DataProvider
+    public static Object[][] branchSessionsProvider() {
+        BranchSession branchSession1 = new BranchSession();
+        branchSession1.setTransactionId(UUIDGenerator.generateUUID());
+        branchSession1.setBranchId(1L);
+        branchSession1.setClientId("c1");
+        branchSession1.setResourceGroupId("my_test_tx_group");
+        branchSession1.setResourceId("tb_1");
+        branchSession1.setLockKey("t:1,2");
+        branchSession1.setBranchType(BranchType.AT);
+        branchSession1.setApplicationId("demo-child-app");
+        branchSession1.setTxServiceGroup("my_test_tx_group");
+        branchSession1.setApplicationData("{\"data\":\"test\"}");
+        branchSession1.setBranchType(BranchType.AT);
+
+        BranchSession branchSession2 = new BranchSession();
+        branchSession2.setTransactionId(UUIDGenerator.generateUUID());
+        branchSession2.setBranchId(2L);
+        branchSession2.setClientId("c1");
+        branchSession2.setResourceGroupId("my_test_tx_group");
+        branchSession2.setResourceId("tb_1");
+        branchSession2.setLockKey("t:1,2");
+        branchSession2.setBranchType(BranchType.AT);
+        branchSession2.setApplicationId("demo-child-app");
+        branchSession2.setTxServiceGroup("my_test_tx_group");
+        branchSession2.setApplicationData("{\"data\":\"test\"}");
+        branchSession2.setBranchType(BranchType.AT);
+        return new Object[][] {{ branchSession1,branchSession2}};
+    }
+}
diff --git a/server/src/test/java/com/alibaba/fescar/server/store/FileTransactionStoreManagerTest.java b/server/src/test/java/com/alibaba/fescar/server/store/FileTransactionStoreManagerTest.java
new file mode 100644
index 000000000..e48de1201
--- /dev/null
+++ b/server/src/test/java/com/alibaba/fescar/server/store/FileTransactionStoreManagerTest.java
@@ -0,0 +1,67 @@
+/*
+ *  Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ *  Licensed 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.alibaba.fescar.server.store;
+
+import com.alibaba.fescar.server.session.FileBasedSessionManager;
+import com.alibaba.fescar.server.session.GlobalSession;
+import com.alibaba.fescar.server.session.SessionManager;
+import org.junit.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+/**
+ * @author tianming.xm@gmail.com
+ * @since 2019/1/25
+ */
+public class FileTransactionStoreManagerTest {
+    private FileTransactionStoreManager fileTransactionStoreManager;
+
+    @BeforeClass
+    public void setUp() throws Exception{
+        SessionManager sessionManager = new FileBasedSessionManager("default","root.data");
+        fileTransactionStoreManager = new FileTransactionStoreManager("root.data",sessionManager);
+    }
+
+    @AfterClass
+    public void tearDown(){
+        fileTransactionStoreManager.shutdown();
+    }
+
+    @Test(dataProvider = "sessionProvider")
+    public void writeSessionTest(GlobalSession globalSession){
+        boolean result = fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.GLOBAL_ADD,globalSession);
+        Assert.assertTrue(result);
+    }
+
+    @Test(dataProvider = "sessionProvider")
+    public void readWriteStoreFromFileTest(GlobalSession globalSession){
+        fileTransactionStoreManager.writeSession(TransactionStoreManager.LogOperation.GLOBAL_ADD,globalSession);
+        List<TransactionWriteStore> stores = fileTransactionStoreManager.readWriteStoreFromFile(100,false);
+        Assert.assertNotNull(stores);
+        Assert.assertTrue(stores.size() > 0);
+    }
+
+    @DataProvider
+    public static Object[][] sessionProvider() {
+        GlobalSession globalSession = new GlobalSession("demo-app","my_test_tx_group","test",6000);
+        return new Object[][] {{ globalSession}};
+    }
+
+}
diff --git a/test/pom.xml b/test/pom.xml
index 674d60f5c..a58a846ad 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -106,7 +106,10 @@
             <artifactId>redisson</artifactId>
             <version>3.9.0</version>
         </dependency>
-
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
 
     </dependencies>